/*---------------------------------------------------------------------------*/
/* sctp.cpp                                                                  */
/* copyright (c) innovaphone 2017                                            */
/*                                                                           */
/*---------------------------------------------------------------------------*/

#include <platform.h>

#include "sdk/common/interface/random.h"
#include "sdk/common/interface/socket.h"
#include "sdk/common/interface/media.h"
#include "sdk/common/interface/appsharing.h"

#include "sctp_rtc.h"

#define RX_WINDOW_CREDIT    81920
#define INBOUND_STREAMS     10
#define OUTBOUND_STREAMS    65535

#define CRC_BIG_ENDIAN 0

#define STRC_DATA(x)   // debug->printf x
#define STRC_WINDOW(x) // debug->printf x

SctpRtc::SctpRtc(class USctpRtc * user, class IInstanceLog * const log, int dropMediaTx, int dropMediaRx) :
    log(log)
{
    this->user = user;
    this->handlingDone = false;
    this->isPeerAlive = false;
    this->initDstTag = 0;
    this->winDstCredit = 0;
    this->inDstStreams = 0;
    this->outDstStreams = 0;
    this->initTsnDst = 0;
    this->cumDstTsnAck = 0;
    this->winSrcCredit = 0;
    this->inSrcStreams = 0;
    this->outSrcStreams = 0;
    this->dataToAcked = 0;
    this->elapsedTimer = 0;
    this->seqNumber = 0;
    this->lastSeqSack = 0;
    this->fwdTsnSupported = false;
    this->lastProcTsnRx = 0;
    this->streamId = 0;
    this->localWinDstCredit = 0;
    this->initTimerWzp = 1;
    this->timerWzp = initTimerWzp;
    this->initWinDstCredit = 0;
    this->heartbeatCnt = 0;
    this->heartbeatCntSent = 0;
    this->doZeroProving = false;
    this->breakHandshake = false;
    this->pInitMsg = NULL;
    this->initMessageLen = 0;
    this->numHandshakeTx = 0;
    this->cookieEchoAckMessage = false;
    this->initAckMessage = false;
    this->lastTsnRx = 0;
    this->timerLastTsnAcked = 0;
    this->sctpServer = false;
    this->dropMediaTx = dropMediaTx;
    this->numAllowPacketsTx = dropMediaTx ? 30 : 0;  // allow sctp handshake to complete
    this->dropSeveralTx = 0;
    this->dropMediaRx = dropMediaRx;
    this->numAllowPacketsRx = dropMediaRx ? 30 : 0;  // allow sctp handshake to complete
    this->dropSeveralRx = 0;
    this->totalRxPackets = 0;
    this->totalTxPackets = 0;
    this->dropRxPackets = 0;
    this->dropTxPackets = 0;
    this->foundGapTsn = 0;
    this->forwardGapTsn = 0;
    this->rxCookieLen = 0;
    this->txCookieLen = 0;

    WRITE_DWORD_NET((unsigned char *)&initSrcTag, IRandom::GetRandom());
    initTsnSrc = initSrcTag;
    lastTsnSrcAcked = initTsnSrc - 1;

    this->txStateCookie = NULL;
    this->rxStateCookie = NULL;
    this->pDataQueue = NULL;

    this->srcSctpNumber = DEFAULT_SCTP_CHANNEL_NUMBER;
    this->dstSctpNumber = 5000;
}

SctpRtc::~SctpRtc()
{
    if(txStateCookie) free(txStateCookie);
    txStateCookie = NULL;
    if (rxStateCookie) free(rxStateCookie);
    rxStateCookie = NULL;
    if(pInitMsg) free(pInitMsg);
    pInitMsg = NULL;

    if(!pTxSctpQueue.empty()) {
        while(pTxSctpQueue.front()) delete pTxSctpQueue.front();
    }

    class RxSctpPackets *pRxSctp = pDataQueue;
    while(pRxSctp) {
        class RxSctpPackets *pSctp_ = pRxSctp;
        pRxSctp = pRxSctp->next;
        if(pSctp_->data) free(pSctp_->data);
        delete pSctp_;
    }
    pDataQueue = NULL;

    if(!this->sctpNumSeqTsns.empty()) {
        while(this->sctpNumSeqTsns.front()) delete this->sctpNumSeqTsns.front();
    }
    if(!pRetrQueue.empty()) {
        while(pRetrQueue.front()) delete pRetrQueue.front();
    }
}

void SctpRtc::SctpTimeout(int timer)
{
    if(pDataQueue != NULL) log->Log(LOG_APPSHARING, "SctpRtc(%p)::TimerOnTimeout %d (%d,%d,%d) num=%d", this, timer, initAckMessage, cookieEchoAckMessage, handlingDone, numHandshakeTx);
    if(timer == 0) {
        if(sctpServer) {
            debug->printf("SctpRtc(%p)::TimerOnTimeout peer did not generate Init Message", this);
            breakHandshake = false;
            GenerateInitMessage();
            SctpHandshakeStart(250);
        }
        else if(!initAckMessage && numHandshakeTx <= 8) {
            if(pInitMsg && initMessageLen) {
                numHandshakeTx++;
                char * iMsg = (char *)malloc(initMessageLen);
                memcpy(iMsg, pInitMsg, initMessageLen); 
                SctpDtlsSend(iMsg, initMessageLen);
                SctpHandshakeStart(250);
            }
            else {
                if(pInitMsg) free(pInitMsg); 
                pInitMsg = NULL;
                GenerateInitMessage();
            }  
        }
        else if(!cookieEchoAckMessage && !handlingDone && numHandshakeTx <= 8) {
            GenerateCookieEchoMessage();
        }
        else {
            debug->printf("SCTP(%p)::SctpTimeout Remote peer not responding to INIT/Cookie Messages", this);
            this->user->SctpRtcConnected(true);
        } 
    }
    else {
        if(pDataQueue != NULL) log->Log(LOG_APPSHARING, "SctpRtc(%p)::TimerOnTimeout t=%d heartbeatCnt=%d pDataQueue=%p", this, elapsedTimer, heartbeatCnt, pDataQueue);
        elapsedTimer++;
        if(doZeroProving) {
            if(timerWzp > 0) timerWzp--;
        }

        heartbeatCnt++;
        if(isPeerAlive == true) {
            // each 5 seconds we send a heartbeat message
            if(heartbeatCnt > 125) {
                heartbeatCnt = 0;
                GenerateHeartbeatMessage();
                heartbeatCntSent++;
            }
            // after 100 seconds we drop messages for this participant
            if(heartbeatCntSent > 20) {
                debug->printf("SCTP(%p)::SctpTimeout Receiver not responding", this);
                isPeerAlive = false;
            }
        }
        SctpRetransmitStart(50);
    }
}

void SctpRtc::SctpSendApplicationData(char * buf, int len, unsigned num)
{
    if(isPeerAlive) {
        sendSctpData(buf, len, num);
    }
    else {
        //STRC(("sctp handshake not ready or abort received!"));
    }
}

bool SctpRtc::sendSctpData(char * buf, int len, unsigned num)
{
    bool firstMsg = true;
    bool result = false;

#if 0
    int off = len/MAX_SCTP_MTU + 1;
    int len2 = p_data->length() + off*28;
    dword t_init1 = kernel->get_timestamp();
#endif

    int maxDataTosend = ((len/MAX_SCTP_MTU)+1)*(31) + len;  // 31 bytes is the maximun header size

#if 1
    if((maxDataTosend > winDstCredit) || ((localWinDstCredit + maxDataTosend) > initWinDstCredit)) {
        STRC_DATA(("SctpRtc::sendSctpData receiver may drop chunks (%x,%d,%d,%d,%d) %d > (%d %d,%d) (%u) seq=%d last-acked=%u", this, fwdTsnSupported, handlingDone, timerWzp, initTimerWzp, maxDataTosend, winDstCredit, initWinDstCredit, localWinDstCredit, initTsnSrc, seqNumber, lastTsnSrcAcked));
    }
#else
    // Data does not pass in the receiver window or data should not pass in the receiver window!
    if((maxDataTosend > winDstCredit) || ((localWinDstCredit + maxDataTosend) > initWinDstCredit)) {
        // Something must be in the queued to be retransmitted, else we send the data...strange but it could happen
        if(pTxSctpQueue.empty() == false) {
            // First time, we do send a prove (we could send data...), leave sometime, maybe an ACK will arrive
            if(!doZeroProving) {
                doZeroProving = true;
                timerWzp = initTimerWzp;
            }
            //debug->printf("SctpRtc::sendSctpData Rx window is too small(%x,%d,%d,%d,%d) %d > (%d %d,%d) (%u) seq=%d last-acked=%u", this, fwdTsnSupported, handlingDone, timerWzp, initTimerWzp, maxDataTosend, winDstCredit, initWinDstCredit, localWinDstCredit, initTsnSrc, seqNumber, lastTsnSrcAcked);
            // Window zero probing, resend last packet!
            if(timerWzp == 0 && (initTimerWzp < 10)) {
                //if(pTxSctpQueue.length() > 200)
                // we don't have any TSN outstanding
                if(windowZeroProbe(seqNumber == 0 ? 65535 : seqNumber - 1, lastTsnSrcAcked) == false) {
                    if(!pTxSctpQueue.empty()) {
                        class SctpMsg * data = pTxSctpQueue.front();
                        windowZeroProbe(READ_WORD_NET(&data->msg[22]), lastTsnSrcAcked);
                    }
                }
                if(timerLastTsnAcked == lastTsnSrcAcked) initTimerWzp++;  // we increment the timer 40 msec
                else initTimerWzp = 1;
                timerWzp = initTimerWzp;
                timerLastTsnAcked = lastTsnSrcAcked;
            }
            return result;
        }
    }
    else doZeroProving = false;
#endif

    //if(((dst_port < 60000) || (dst_port > 60010)) && ((len >= MAX_SCTP_MTU) || padding(len)))
    //debug->printf("SctpRtc::sendSctpData [%x] sctp(%u) send data seq=%u init_tsn_src=%u inno-seq=%u", this, maxDataTosend, seqNumber, initTsnSrc, READ_WORD_NET(&buf[2]));

    word tSeqNumber = seqNumber++;
    int indexData = 0;
    int startTsn = initTsnSrc;
    int totDataSent = 0;
    //bool complete = true; 
    while(len > 0) {
        int dataTosent = MIN(len, MAX_SCTP_MTU);
        len -= dataTosent;
        int bufferLength = 12+16+dataTosent+4;
        byte *data = (byte *)malloc(bufferLength);
        if(data) {
            WRITE_WORD_NET(&data[0], srcSctpNumber);
            WRITE_WORD_NET(&data[2], dstSctpNumber);
            WRITE_DWORD_NET(&data[4], initDstTag);         // verification tag
            WRITE_DWORD_NET(&data[8], 0);                  // checksum
            data[12] = DataMsg;                            // Data
            data[13] = 0x0;                                // 0x0, ordered! Flags, 0x4 -> Unordered delivery
            if(firstMsg) {
                data[13] |= 0x2;                           // Flags, first message
                firstMsg = false;
            }
            if(len <= 0) {
                data[13] |= 0x1;                           // Flags, last message            
            }
            WRITE_WORD_NET(&data[14], dataTosent+16);      // Length
            WRITE_DWORD_NET(&data[16], initTsnSrc);        // Cumulative TSN ACK
            sequenceTable[initTsnSrc & 0xfff] = tSeqNumber;
            initTsnSrc++;
            WRITE_WORD_NET(&data[20], streamId);           // Stream identifier
            WRITE_WORD_NET(&data[22], tSeqNumber);         // Stream sequence number
            WRITE_DWORD_NET(&data[24], 53);                // Protocol Identifier, WebRTC Binary Partial
            //https://tools.ietf.org/html/draft-ietf-rtcweb-data-channel-13
            memcpy(&data[28], &buf[indexData], dataTosent);
            indexData += dataTosent;

            int padding = ((dataTosent+12+4) % 4);
            if(padding) {
                for(int l = 0; l<(4-padding); l++) {
                    data[12 + 16 + dataTosent] = 0x0;
                    dataTosent++;
                }
            }
            int dToSend = 12+16+dataTosent;
            dword crc32 = generateCrc32c(data, dToSend);
#if CRC_BIG_ENDIAN
            WRITE_DWORD_NET(&data[8], crc32);
#else
            WRITE_DWORD_LE(&data[8], crc32);
#endif
            STRC_DATA(("sctp data(%x) (%x,%d,%d,%d) flags=%x padd=%d seq=%d tsn=%u last=%u data(%d)=%x,%x rem=%d num=%x localWinDstCredit=%u,tempWinDstCredit=%u", this, data, bufferLength, len, dataTosent, data[13], padding, tSeqNumber, (initTsnSrc - 1), lastTsnSrcAcked, 12 + 16 + dataTosent - 4, READ_DWORD_NET(&data[12 + 16]), READ_DWORD_NET(&data[12 + 16 + dataTosent - 4]), bufferLength - dToSend, num, localWinDstCredit, tempWinDstCredit));

            class SctpMsg * sctpMsg = new class SctpMsg((char *)data, dToSend);
            pTxSctpQueue.push_back(sctpMsg);
            localWinDstCredit += dToSend;
            tempWinDstCredit -= dToSend;

            if(dropMediaTx) {
                totalTxPackets++;
                if(dropSeveralTx || (--numAllowPacketsTx <= 0)) {
                    //debug->printf("SctpRtc(%p)::SctpDtlsSend drop messages len=%d tsn=%u numAllowPackets=%d dropSeveral=%d total=%d drop=%d perc=%d", this, len, READ_DWORD_NET(&data[16]), numAllowPacketsTx, dropSeveralTx, totalTxPackets, dropTxPackets, (totalTxPackets * 100) / dropTxPackets);
                    if(dropSeveralTx) dropSeveralTx--;
                    else {
                        dropSeveralTx = (IRandom::GetRandom() % 4) + 1;
                        numAllowPacketsTx = (100 * dropSeveralTx) / dropMediaTx;
                        //debug->printf("SctpRtc(%p)::SctpDtlsSend drop messages numAllowPackets=%d dropSeveral=%d total=%d drop=%d perc=%d", this, numAllowPacketsTx, dropSeveralTx, totalTxPackets, dropTxPackets, (dropTxPackets * 100) / totalTxPackets);
                    }
                    //complete = false;
                    dropTxPackets++;
                    continue;
                }
            }
            SctpDtlsSend((char *)data, dToSend);
            totDataSent += dToSend;
        }
    }
    //debug->printf("SctpRtc(%p)::SctpDtlsSend seq=%d tsnStart=%u tsnEnd=%u num=%x complete=%d", this, tSeqNumber, startTsn, initTsnSrc - 1, num, complete);
    sctpNumSeqTsns.push_back(new class SctpNumSeqTsn(num, tSeqNumber, startTsn, initTsnSrc - 1, totDataSent));

#if 0
    if(len2 > 30000) {
        dword t_last = kernel->get_timestamp();
        dword t_diff = t_last-t_init1;
        debug->printf("bitrate(%x) %d %d %d.%03iMbyte/s", this, len2, t_diff, len2/((1000/8)*t_diff),(len2*8/t_diff)%1000);
    }
#endif

    return result;
}

void SctpRtc::SctpRecv(char * buf, int len)
{
    if (validateCrc32((byte *)buf, len) == false) {
        if(len >= 24) {
            debug->printf("SCTP(%p)::SctpRecv Failed to validate Checksum! %u %u (%d)%x", this, READ_DWORD_NET(&buf[16]), READ_WORD_NET(&buf[22]), len, READ_DWORD_NET(&buf[len-4]));
        }
        else {
            debug->printf("SCTP(%p)::SctpRecvFailed to validate Checksum! %d", this, len);
        }
        return;
    }
    if (len < 16) {
        debug->printf("SCTP(%p)::SctpRecv packet too small %d", this, len);
        return;
    }

    dword verTag = READ_DWORD_NET(&buf[4]);
    if (verTag != initSrcTag) {
        if(verTag == 0 && (unsigned int)buf[12] == InitMsg) {
            debug->printf("SCTP(%p)::SctpRecv Init Message duplicate", this);
        }
        else {
            debug->printf("SCTP(%p)::SctpRecv Verification Tag is wrong %x != %x Message=%d", this, verTag, initSrcTag, buf[12]);
            return;
        }
    }

    if(srcSctpNumber != READ_WORD_NET(&buf[2]) || dstSctpNumber != READ_WORD_NET(&buf[0])) {
        debug->printf("SCTP(%p)::SctpRecv SCTP ports do not match %d!=%d %d!=%d", this, srcSctpNumber, READ_WORD_NET(&buf[2]), dstSctpNumber, READ_WORD_NET(&buf[0]));
        return;    
    }

    // we accept any message at the indication of life
    heartbeatCntSent = 0;
    heartbeatCnt = 0;
    isPeerAlive = true;

    // Bundle
    int indPacket = 12;
    while ((len - indPacket) > 0) {
        int procLen = 0;
        byte rMsg = (byte)buf[indPacket];

        //debug->printf("SCTP(%p)::SctpRecv SCTP len=%d (%d) rMsg %u len=%d tsn=%u seq=%u", this, len, indPacket, rMsg, READ_WORD_NET(&buf[indPacket + 2]), READ_DWORD_NET(&buf[indPacket + 4]), READ_WORD_NET(&buf[indPacket + 10]));

        switch (rMsg) {
            case DataMsg:
                if(handlingDone) {
                    procLen = ProcessData((byte *)&buf[indPacket], len - indPacket);
                }
                else {
                    debug->printf("SCTP(%p)::SctpRecv handshake not completed yet!", this);
                    return;
                }
                break;
            case InitMsg:
                if(len < 28) {
                    debug->printf("SCTP(%p)::SctpRecv init message too small %d", this, len);
                    break;
                }
                // This INIT message is maybe a reconfiguration...
                clearSctpData();

                procLen = ProcessInit((byte *)&buf[indPacket], len - indPacket);
                GenerateInitAckMessage();
                SctpRetransmitStart(40);
                break;
            case InitAckMsg:
                initAckMessage = true;
                SctpHandshakeStop();
                numHandshakeTx = 0;
                procLen = ProcessInitAck((byte *)&buf[indPacket], len - indPacket);
                SctpRetransmitStart(40);
                break;
            case DataAckMsg:
                procLen = ProcessSack((byte *)&buf[indPacket], len - indPacket);
                break;
            case EchoMsg:
                cookieEchoAckMessage = true;
                SctpHandshakeStop();
                numHandshakeTx = 0;
                procLen = ProcessCookieEcho((byte *)&buf[indPacket], len - indPacket);
                debug->printf("SCTP(%p)::SctpRecv COOKIE-ECHO Received (%d,%d,%d)", this, len, indPacket, procLen);
                break;
            case EchoAckMsg:
                debug->printf("SCTP(%p)::SctpRecv COOKIE-ACK Received", this);
                procLen = 4;
                handlingDone = true;
                SctpHandshakeStop();
                GenerateWebrtcDcepMessage();
                handlingDone = true;
                break;
            case HeartbeatMsg:
                debug->printf("SCTP(%p)::SctpRecv HEARTBEAT Received", this);
                procLen = ProcessHeartbeat((byte *)&buf[indPacket], len - indPacket);
                break;
            case HeartbeatAckMsg:
                debug->printf("SCTP(%p)::SctpRecv HEARTBEAT ACK Received", this);
                procLen = ProcessAckHeartbeat((byte *)&buf[indPacket], len - indPacket);
                break;
            case ErrorMsg:
                debug->printf("SCTP(%p)::SctpRecv Error Received", this);
                procLen = ProcessError((byte *)&buf[indPacket], len - indPacket);
                break;
            case AbortMsg:
                debug->printf("SCTP(%p)::SctpRecv ABORT Received", this);
                procLen = ProcessAbort((byte *)&buf[indPacket], len - indPacket);
                handlingDone = false;
                isPeerAlive = false;
                return;
            case ShutdownMsg:
                debug->printf("SCTP(%p)::SctpRecv SHUTDOWN Received", this);
                procLen = 8;
                cumDstTsnAck = READ_DWORD_NET(&buf[indPacket + 4]);
                if(0) {  // check cum_dst_tsn_ack. If wrong send ABORT message!
                }
                else {
                    GenerateShutdownAckMessage();
                }
                SctpRetransmitStop();
                break;
            case ShutdownAckMsg:
                procLen = 4;
                debug->printf("SCTP(%p)::SctpRecv SHUTDOWN-ACK Received", this);
                GenerateShutdownCompleteMessage();
                SctpRetransmitStop();
                // Clean Up queues!!
                break;
            case ShutdownOkMsg:
                debug->printf("SCTP(%p)::SctpRecv SHUTDOWN Complete Received", this);
                handlingDone = false;
                isPeerAlive = false;
                return;
            case ForwardCumTsnMsg:
                procLen = ProcessForwardTsn((byte *)&buf[indPacket], len - indPacket);
                break;
            default:
                debug->printf("SCTP(%p)::SctpRecv Unknown SCTP message %x %x %x", this, buf[0], buf[1], buf[12]);
                return;
        }
        indPacket += procLen;
    }
}

bool SctpRtc::GenerateCookieEchoMessage(void)
{
    if(breakHandshake || !rxStateCookie) return false;

    bool result = false;
    int len = 12 + 4 + rxCookieLen;   // 
    byte * data = (byte *)malloc(len * sizeof(byte));

    numHandshakeTx++;
    SctpHandshakeStart(250);

    if(data) {
        WRITE_WORD_NET(&data[0], srcSctpNumber);
        WRITE_WORD_NET(&data[2], dstSctpNumber);
        WRITE_DWORD_NET(&data[4], initDstTag);    // verification tag
        WRITE_DWORD_NET(&data[8], 0);               // checksum
        data[12] = 10;                              // Cookie echo
        data[13] = 0;                               // Flags
        WRITE_WORD_NET(&data[14], (rxCookieLen + 4));
        memcpy(&data[16], rxStateCookie, rxCookieLen);
        unsigned int crc32 = generateCrc32c(data, len);
#if CRC_BIG_ENDIAN
        WRITE_DWORD_NET(&data[8], crc32);
#else
        WRITE_DWORD_LE(&data[8], crc32);
#endif
        SctpDtlsSend((char *)data, len);

        debug->printf("SCTP(%p)::GenerateCookieEchoMessage (%d,%d) rxCookieLen=%d", this, len, result, rxCookieLen);
    }
    return result;
}

bool SctpRtc::GenerateCookieAckMessage(void)
{
    bool result = false;
    int len = 12 + 4;
    byte *data = (byte *)malloc(len);
    if(data) {
        WRITE_WORD_NET(&data[0], srcSctpNumber);
        WRITE_WORD_NET(&data[2], dstSctpNumber);
        WRITE_DWORD_NET(&data[4], initDstTag);    // verification tag
        WRITE_DWORD_NET(&data[8], 0);             // checksum
        data[12] = EchoAckMsg;                    // Cookie ack
        data[13] = 0;                             // Flags
        WRITE_WORD_NET(&data[14], 4);
        dword crc32 = generateCrc32c(data, len);
#if CRC_BIG_ENDIAN
        WRITE_DWORD_NET(&data[8], crc32);
#else
        WRITE_DWORD_LE(&data[8], crc32);
#endif
        SctpDtlsSend((char *)data, len);
        debug->printf("SCTP(%p)::GenerateCookieAckMessage(%d)", this, len);
        handlingDone = true;
        result = true;
    }
    return result;
}

bool SctpRtc::GenerateShutdownAckMessage(void)
{
    int len = 16;
    byte *data = (byte *)malloc(len);

    WRITE_WORD_NET(&data[0], srcSctpNumber);
    WRITE_WORD_NET(&data[2], dstSctpNumber);
    WRITE_DWORD_NET(&data[4], initDstTag);      // verification tag
    WRITE_DWORD_NET(&data[8], 0);               // checksum
    data[12] = ShutdownAckMsg;                  // shutdown-ack
    data[13] = 0;                               // Flags
    WRITE_WORD_NET(&data[14], 4);               // Length
    dword crc32 = generateCrc32c(data, len);
#if CRC_BIG_ENDIAN
    WRITE_DWORD_NET(&data[8], crc32);
#else
    WRITE_DWORD_LE(&data[8], crc32);
#endif
    SctpDtlsSend((char *)data, len);
    handlingDone = false;
    debug->printf("SCTP(%p)::GenerateShutdownAckMessage(%d)", this, len);

    return true;
}

bool SctpRtc::GenerateShutdownCompleteMessage(void)
{
    int len = 16;
    byte *data = (byte *)malloc(len);

    WRITE_WORD_NET(&data[0], srcSctpNumber);
    WRITE_WORD_NET(&data[2], dstSctpNumber);
    WRITE_DWORD_NET(&data[4], initDstTag);      // verification tag
    WRITE_DWORD_NET(&data[8], 0);               // checksum
    data[12] = 14;                              // shutdown-complete
    data[13] = 0;                               // Flags
    WRITE_WORD_NET(&data[14], 4);               // Length
    dword crc32 = generateCrc32c(data, len);
#if CRC_BIG_ENDIAN
    WRITE_DWORD_NET(&data[8], crc32);
#else
    WRITE_DWORD_LE(&data[8], crc32);
#endif
    SctpDtlsSend((char *)data, len);
    handlingDone = false;
    debug->printf("SCTP(%p)::GenerateShutdownCompleteMessage(%d)", this, len);

    return true;
}

bool SctpRtc::GenerateHeartbeatAckMessage(byte *heartbeat, int hblen)
{
    bool result = false;
    int len = 12 + 4 + hblen;
    int padding = ((hblen + 4) % 4);
    if(padding) len += (4 - padding);

    byte *data = (byte *)malloc(len);

    //STRC_DATA(("Send Heartbeat Ack Message(%d) type=%d hlen=%d len=%d,%d", hblen, READ_WORD_NET(&heartbeat[0]), READ_WORD_NET(&heartbeat[2]), hblen, len));

    if(data) {
        WRITE_WORD_NET(&data[0], srcSctpNumber);
        WRITE_WORD_NET(&data[2], dstSctpNumber);
        WRITE_DWORD_NET(&data[4], initDstTag);      // verification tag
        WRITE_DWORD_NET(&data[8], 0);               // checksum
        data[12] = HeartbeatAckMsg;                 // HEARTBEAT ACK
        data[13] = 0;                               // Flags
        WRITE_WORD_NET(&data[14], (hblen + 4));
        memcpy(&data[16], heartbeat, hblen);
        dword crc32 = generateCrc32c(data, len);
#if CRC_BIG_ENDIAN
        WRITE_DWORD_NET(&data[8], crc32);
#else
        WRITE_DWORD_LE(&data[8], crc32);
#endif
        SctpDtlsSend((char *)data, len);
        debug->printf("SCTP(%p)::GenerateHeartbeatAckMessage(%d)", this, len);
        result = true;
    }
    return result;
}

bool SctpRtc::GenerateHeartbeatMessage(void)
{
    bool result = false;
    int len = 12 + 4 + 64;

    byte *data = (byte *)malloc(len);

    if(data) {
        WRITE_WORD_NET(&data[0], srcSctpNumber);
        WRITE_WORD_NET(&data[2], dstSctpNumber);
        WRITE_DWORD_NET(&data[4], initDstTag);      // verification tag
        WRITE_DWORD_NET(&data[8], 0);               // checksum
        data[12] = HeartbeatMsg;                    // HEARTBEAT ACK
        data[13] = 0;                               // Flags
        WRITE_WORD_NET(&data[14], 68);
        for(int i=0; i<16; i++) {
            WRITE_DWORD_NET(&data[16+(i<<2)], IRandom::GetRandom());
        }
        dword crc32 = generateCrc32c(data, len);
#if CRC_BIG_ENDIAN
        WRITE_DWORD_NET(&data[8], crc32);
#else
        WRITE_DWORD_LE(&data[8], crc32);
#endif
        SctpDtlsSend((char *)data, len);
        debug->printf("SCTP(%p)::GenerateHeartbeatMessage(%d)", this, len);
        result = true;
    }
    return result;
}

void SctpRtc::DoSctpHandshake(bool server, word dstSctpNumber, word srcSctpNumber)
{
    debug->printf("SCTP(%p)::DoSctpHandshake server=%d sctpNumber=%d/%d", this, server, dstSctpNumber, srcSctpNumber);

    this->sctpServer = server;
    this->dstSctpNumber = dstSctpNumber;
    this->srcSctpNumber = srcSctpNumber;

    if(!server) {
        this->breakHandshake = false;
        GenerateInitMessage();
        SctpHandshakeStart(250);
    }
    else SctpHandshakeStart(1000);
}

bool SctpRtc::GenerateInitMessage(void)
{
    numHandshakeTx++;
    SctpHandshakeStart(250);

    debug->printf("SCTP(%p)::GenerateInitMessage %d -> %d", this, srcSctpNumber, dstSctpNumber);

    // generate INIT Message
    byte * data = (byte *)malloc(256);
    int len = 0;

    WRITE_WORD_NET(&data[0], srcSctpNumber);
    WRITE_WORD_NET(&data[2], dstSctpNumber);
    // Verification Tag
    data[4] = data[5] = data[6] = data[7] = 0;
    // CRC
    data[8] = 0x0; data[9] = 0x0; data[10] = 0x0; data[11] = 0x0; 
    // Chunk
    data[12] = 0x1;                                // Init Chunk
    data[13] = 0x0;                                // Flags
    //WRITE_WORD_NET(&data[14], 28);               // Length  (12 + 28)
    WRITE_DWORD_NET(&initSrcTag, IRandom::GetRandom());

    unsigned int tmpInitTag = IRandom::GetRandom();

    initSrcTag = (initSrcTag ^ tmpInitTag);
    WRITE_DWORD_NET(&data[16], initSrcTag);      // Initiative Tag
    winSrcCredit = RX_WINDOW_CREDIT;
    WRITE_DWORD_NET(&data[20], winSrcCredit);    // Window credit
    inSrcStreams = INBOUND_STREAMS;
    WRITE_WORD_NET(&data[24], inSrcStreams);
    outSrcStreams = OUTBOUND_STREAMS;
    WRITE_WORD_NET(&data[26], outSrcStreams);
    initTsnSrc = initSrcTag;
    WRITE_DWORD_NET(&data[28], initTsnSrc);      // Initial TSN, MAY be set to the value of the Initiate Tag field
    len = 32;

    // Forward TSN Supported
    WRITE_WORD_NET(&data[len+0], 49152);
    WRITE_WORD_NET(&data[len+2], 4);
    len += 4;

    // Supported extension Parameters
    WRITE_WORD_NET(&data[len+0], 32776);
    WRITE_WORD_NET(&data[len+2], 5);
    data[len+4] = 192;  // Forward TSN
    data[len+5] = 0;
    data[len+6] = 0;
    data[len+7] = 0;
    len += 8;

    // Supported Address Types
    WRITE_WORD_NET(&data[len+0], 12);
    WRITE_WORD_NET(&data[len+2], 6);                  // Length
    WRITE_WORD_NET(&data[len+4], 5);                  // Address Type, IPv4
    data[len+6] = data[len+7] = 0;                    // Padding
    len += 8;

    WRITE_WORD_NET(&data[14], len-12);                // Length  (12 + 28)
    unsigned int crc32 = generateCrc32c(data, len);
#if CRC_BIG_ENDIAN
    WRITE_DWORD_NET(&data[8], crc32);
#else
    WRITE_DWORD_LE(&data[8], crc32);
#endif

    pInitMsg = (byte *)malloc(len); 
    if(pInitMsg) {
        memcpy(pInitMsg, data, len);
        initMessageLen = len;
    }

    bool result = false;
    SctpDtlsSend((char *)data, len); 

    debug->printf("SCTP(%p)::Send Init Message(%u,%d,%d,%d)", this, initTsnSrc, seqNumber, len, result);
    lastTsnSrcAcked = initTsnSrc-1;

    return result;
}

bool SctpRtc::GenerateInitAckMessage(void)
{
    bool result = false;
    txCookieLen = 228;

    int len = 12 + (16 + 4) + (4 + txCookieLen);
    if(fwdTsnSupported) {
        len += 12;
    }
    byte *data = (byte *)malloc(len);
    if (data) {
        WRITE_WORD_NET(&data[0], srcSctpNumber);
        WRITE_WORD_NET(&data[2], dstSctpNumber);
        WRITE_DWORD_NET(&data[4], initDstTag);         // verification tag
        WRITE_DWORD_NET(&data[8], 0);                  // checksum
        data[12] = InitAckMsg;                         // INIT ACK
        data[13] = 0;                                  // Flags

        WRITE_WORD_NET(&data[14], len - 12);           // Length
        WRITE_DWORD_NET(&data[16], initSrcTag);        // Initiative Tag
        winSrcCredit = RX_WINDOW_CREDIT;
        WRITE_DWORD_NET(&data[20], winSrcCredit);      // Window credit
        outSrcStreams = MIN(outDstStreams, OUTBOUND_STREAMS);
        WRITE_WORD_NET(&data[26], outSrcStreams);
        inSrcStreams = MIN(inDstStreams, INBOUND_STREAMS);
        WRITE_WORD_NET(&data[24], inSrcStreams);
        WRITE_DWORD_NET(&data[28], initTsnSrc);        // Initial TSN, MAY be set to the value of the Initiate Tag field

        // Cookie Parameter could already be initialized
        if(txStateCookie == NULL) {
            txStateCookie = (byte *)malloc(txCookieLen);
            if (txStateCookie) {
                randBytes((unsigned char *)txStateCookie, txCookieLen);
            }
        }
        int offset;
        if(txStateCookie) {
            offset = 4+ txCookieLen;
            WRITE_WORD_NET(&data[32], 7);
            WRITE_WORD_NET(&data[34], txCookieLen + 4);
            memcpy(&data[36], txStateCookie, txCookieLen *sizeof(byte));
        }
        else offset = 0;
        if(fwdTsnSupported) {
            WRITE_WORD_NET(&data[32+offset], 49152);
            WRITE_WORD_NET(&data[34+offset], 4);
            WRITE_WORD_NET(&data[36+offset], 32776);
            WRITE_WORD_NET(&data[38+offset], 5);
            data[40+offset] = 192;
            data[41+offset] = data[42+offset] = data[43+offset] = 0;
        }

        dword crc32 = generateCrc32c(data, len);
#if CRC_BIG_ENDIAN
        WRITE_DWORD_NET(&data[8], crc32);
#else
        WRITE_DWORD_LE(&data[8], crc32);
#endif
        SctpDtlsSend((char *)data, len);
        debug->printf("SCTP(%p)::GenerateInitAckMessage(%d) tsn %u fwd %d", this, len, initTsnSrc, fwdTsnSupported);
        result = true;
        breakHandshake = true;
        lastTsnSrcAcked = initTsnSrc - 1;
    }
    return result;
}

// we do not inform about packet lost
// we will recognize later on packet lost at application level
void SctpRtc::GenerateSackMessage(unsigned int rTsn)
{
    int len = 12 + 16;
    byte *data = (byte *)malloc(len);
    if(!data) {
        cumDstTsnAck = rTsn;
        return;
    }

    WRITE_WORD_NET(&data[0], srcSctpNumber);
    WRITE_WORD_NET(&data[2], dstSctpNumber);
    WRITE_DWORD_NET(&data[4], initDstTag);         // verification tag
    WRITE_DWORD_NET(&data[8], 0);                  // checksum
    data[12] = 3;                                  // SACK
    data[13] = 0;                                  // Flags
    WRITE_WORD_NET(&data[14], 16);                 // Length
    WRITE_DWORD_NET(&data[16], rTsn);              // Cumulative TSN ACK
    WRITE_DWORD_NET(&data[20], (winSrcCredit > dataToAcked) ? (winSrcCredit - dataToAcked) : 0); // Window
    WRITE_WORD_NET(&data[24], 0);                  // Number of GAP
    WRITE_WORD_NET(&data[26], 0);                  // Number of duplicated TSNs

    dword crc32 = generateCrc32c(data, len);
#if CRC_BIG_ENDIAN
    WRITE_DWORD_NET(&data[8], crc32);
#else
    WRITE_DWORD_LE(&data[8], crc32);
#endif
    SctpDtlsSend((char *)data, len);

    //debug->printf("SCTP(%p)::generateSackMessage(%d) tsn %d win %d", this, len, rTsn, (winSrcCredit > dataToAcked) ? (winSrcCredit - dataToAcked) : 0);
}

void SctpRtc::GenerateSackMessageWithGaps(void)
{
    int numDups = 0;
    int numGaps = 0;
    unsigned int firstTsn = cumDstTsnAck;

    // Calculate Duplicates and Gaps
    class RxSctpPackets *pTmp = pDataQueue;
    while(pTmp) {
        if(pTmp->acked) {  // pTmp->tsn < cum_dst_tsn_ack, they are ACKed but not processed!
            firstTsn = pTmp->tsn;
            pTmp = pTmp->next;
            continue;
        }
        //debug->printf("GenerateSackMessageWithGaps %u %u (%u)", pDataQueue->tsn, pTmp->tsn, cumDstTsnAck);
        if(pTmp->dup) {
            //printf("found dup %u (%u)\n", pTmp->dup, pTmp->tsn);
            numDups++;
        }
        if((firstTsn + 1) != pTmp->tsn) {
            if(!numGaps) { // for tracing purposes only
                if(foundGapTsn != (firstTsn + 1)) {
                    STRC_DATA(("GenerateSackMessageWithGaps found gap(%x,%u) %u -> %u (%u) cumDstTsnAck=%u", this, numGaps, firstTsn, pTmp->tsn, pTmp->acked, cumDstTsnAck));
                }
                foundGapTsn = firstTsn + 1;
                forwardGapTsn = 0;
            }
            numGaps++;
        }
        else if(!numGaps) {                  // cum_dst_tsn_ack and pTmp->acked always together
            cumDstTsnAck = pTmp->tsn;
            pTmp->acked = true;              // acknowlegde packets before a gap
        }
        firstTsn = pTmp->tsn;                // Cumulative TSN ACK
        pTmp = pTmp->next;
    }

    int len = 12 + 16 + numDups * 4 + numGaps * 4;
    byte *data = (byte *)malloc(len);
    if(!data) {
        return;
    }
    WRITE_WORD_NET(&data[0], srcSctpNumber);
    WRITE_WORD_NET(&data[2], dstSctpNumber);
    WRITE_DWORD_NET(&data[4], initDstTag);         // verification tag
    WRITE_DWORD_NET(&data[8], 0);                  // checksum
    data[12] = DataAckMsg;                         // SACK
    data[13] = 0;                                  // Flags
    WRITE_WORD_NET(&data[14], 16 + numDups * 4 + numGaps * 4); // Length
    WRITE_DWORD_NET(&data[16], cumDstTsnAck);     // Cumulative TSN ACK
    WRITE_DWORD_NET(&data[20], (winSrcCredit > dataToAcked) ? (winSrcCredit - dataToAcked) : 0); // Window
    WRITE_WORD_NET(&data[24], numGaps);           // Number of GAP
    WRITE_WORD_NET(&data[26], numDups);           // Number of duplicated TSNs

    int indPacket = 28;
    if(numGaps) {
#if 0
        if(numGaps > 1) {
            debug->printf("GenerateSackMessageWithGaps found gaps (%d) ack=%u origTsn=%u", numGaps, cumDstTsnAck, origTsn);
        }
#endif
        int runGap = 0;
        word diff = 0;
        firstTsn = cumDstTsnAck;
        indPacket -= 4;    // will be incremented first time a gap is found
        pTmp = pDataQueue;
        while(pTmp) {
            if(pTmp->acked) {  // pTmp->tsn < cum_dst_tsn_ack, they are ACKed but not processed!
                firstTsn = pTmp->tsn;
                pTmp = pTmp->next;
                continue;
            }
            if((firstTsn + 1) != pTmp->tsn) {
#if 0
                if(runGap) {
                    debug->printf("GenerateSackMessageWithGaps gap end cumDstTsnAck=%u lastOk=%u tsn=%u (%u,%u)", cumDstTsnAck, firstTsn, pTmp->tsn, READ_WORD_NET(&data[indPacket]), READ_WORD_NET(&data[indPacket + 2]));
                }
#endif
                indPacket += 4;
                diff = (pTmp->tsn - cumDstTsnAck);
                WRITE_WORD_NET(&data[indPacket], diff);
                WRITE_WORD_NET(&data[indPacket+2], diff);
                //debug->printf("GenerateSackMessageWithGaps gap cumDstTsnAck=%u lastOk=%u tsn=%u (%u,%u) len=%d, numGaps=%d", cumDstTsnAck, firstTsn, pTmp->tsn, READ_WORD_NET(&data[indPacket]), READ_WORD_NET(&data[indPacket + 2]), len, numGaps);
                runGap = 1;
            }
            else {
                if(runGap) {
                    WRITE_WORD_NET(&data[indPacket+2], diff + runGap);
                    runGap++;
#if 0
                    if(pTmp->next == NULL) {
                        debug->printf("GenerateSackMessageWithGaps gap end2 cumDstTsnAck=%u lastOk=%u tsn=%u (%u,%u)", cumDstTsnAck, firstTsn, pTmp->tsn, READ_WORD_NET(&data[indPacket]), READ_WORD_NET(&data[indPacket + 2]));
                    }
#endif
                }
            }
            firstTsn = pTmp->tsn;    // Cumulative TSN ACK
            pTmp = pTmp->next;
        }
        indPacket += 4;
    }
    if(numDups) {
        pTmp = pDataQueue;
        while(pTmp) {
            if(pTmp->dup) {
                WRITE_DWORD_NET(&data[indPacket], pTmp->tsn);
                indPacket += 4;
            }
            pTmp = pTmp->next;
        }
    }

    dword crc32 = generateCrc32c(data, len);
#if CRC_BIG_ENDIAN
    WRITE_DWORD_NET(&data[8], crc32);
#else
    WRITE_DWORD_LE(&data[8], crc32);
#endif
    SctpDtlsSend((char *)data, len);
    log->Log(LOG_APPSHARING, "SCTP Send Ack Message(%x,%d == %d) %u gaps %d", this, len, indPacket, cumDstTsnAck, numGaps);

    ProcessDataQueue();
}

void SctpRtc::ProcessDataQueue(void)
{
    class RxSctpPackets *pBitStart = NULL;
    int totalLength = 0;
    class RxSctpPackets *pTmp = pDataQueue;
    while(pTmp) {
        class RxSctpPackets *pTmpNext = pTmp->next;
        STRC_DATA(("SCTP(%p)::ProcessDataQueue pTmp=%p pTmp->tsn=%u pTmpNext=%p s=%d,e=%d pBitStart=%p", this, pTmp, pTmp->tsn, pTmpNext, pTmp->start, pTmp->end, pBitStart));
        if(pTmp->acked) {
            if(pTmp->start) {
                if(pBitStart) {
                    STRC_DATA(("SCTP(%p)::GenerateSackMessageWithGaps gaps, end bit missing queue=%u actual=%u cumDstTsnAck=%u (%p == %p)", this, pDataQueue->tsn, pTmp->tsn, cumDstTsnAck, pDataQueue, pBitStart));
                    // from the beginning of the queue to actual one! pDataQueue must be equal to pBitStart
                    class RxSctpPackets *pTmpTmp = pDataQueue;
                    while(pTmpTmp != pTmp) {
                        class RxSctpPackets *pTmpTmpN = pTmpTmp->next;
                        dataToAcked -= pTmpTmp->len;
                        free(pTmpTmp->data);
                        lastProcTsnRx = pTmpTmp->tsn;
                        delete pTmpTmp;
                        pTmpTmp = pTmpTmpN;
                    }
                    pDataQueue = pTmp;
                    pDataQueue->prev = NULL;
                }
                pBitStart = pTmp;
                totalLength = pTmp->len;
            }
            else {
                if(!pBitStart) {
                    STRC_DATA(("SCTP(%p)::GenerateSackMessageWithGaps start bit missing! %u totalLength=%d", this, pTmp->tsn, totalLength));
                    dataToAcked -= pTmp->len;
                    free(pTmp->data);
                    lastProcTsnRx = pTmp->tsn;
                    delete pTmp;
                    pTmp = pTmpNext;
                    pDataQueue = pTmp;
                    if(pDataQueue) pDataQueue->prev = NULL;
                    totalLength = 0;
                    continue;
                }
                totalLength += pTmp->len;
            }
            if(pTmp->end) {
                if(!pBitStart) { // This should not happen!
                    debug->printf("SCTP(%p)::GenerateSackMessageWithGaps bit start not found! (should not happen!) %u", this, pTmp->tsn);
                    dataToAcked -= pTmp->len;
                    free(pTmp->data);
                    lastProcTsnRx = pTmp->tsn;
                    delete pTmp;
                    pTmp = pTmpNext;
                    pDataQueue = pTmp;
                    if(pDataQueue) pDataQueue->prev = NULL;
                    totalLength = 0;
                    continue;
                }
                if(pTmp == pBitStart) {  // start/end in the same packet
                    // Send to conference interface
                    // WebRTC DCEP
                    if(pTmp->prot == 50) {
                        if(pTmp->data[0] == 0x3) ProcessWebrtcDcepMsg(pTmp->data, pTmp->len, pTmp->streamId);
                        else ProcessWebrtcDcepOkMsg(pTmp->data, pTmp->len);
                    }
                    else if(pTmp->prot == 53 || pTmp->prot == 52 || pTmp->prot == 51) {  // WebRTC binary partial
                        SctpRecvApplicationData((char *)pTmp->data, pTmp->len);
                    }
                    else {
                        debug->printf("SCTP(%p)::GenerateSackMessageWithGaps no payload type recognized! %d", this, pTmp->prot);
                    }
                    free(pTmp->data);
                    lastProcTsnRx = pTmp->tsn;
                    dataToAcked -= pTmp->len;
                    delete pTmp;
                    pDataQueue = pTmpNext;
                    if (pDataQueue) pDataQueue->prev = NULL;
                    STRC_DATA(("SCTP(%p)::ProcessDataQueue ok pDataQueue=%p", this, pDataQueue));
                }
                else {
                    int numLost = 0;
                    unsigned int checkingTsn = pBitStart->tsn;
                    class RxSctpPackets *pTmpTmp = pBitStart->next;
                    while(pTmpTmp != pTmp) {
                        numLost = (0xffffffff + pTmpTmp->tsn - checkingTsn) & 0xffffffff;
                        if(numLost) {
                            break;
                        }
                        checkingTsn = pTmpTmp->tsn;
                        pTmpTmp = pTmpTmp->next;
                    }
                    numLost += ((0xffffffff + pTmp->tsn - checkingTsn) & 0xffffffff);
                    if(numLost) {
                        STRC_DATA(("SCTP(%p)::GenerateSackMessageWithGaps gaps, found lost(%d) %u %u", this, numLost, pTmp->tsn, lastProcTsnRx));
                        class RxSctpPackets *pTmpTmp = pBitStart;
                        while(pTmpTmp != pTmp) {
                            class RxSctpPackets *pTmpTmpN = pTmpTmp->next;
                            dataToAcked -= pTmpTmp->len;
                            free(pTmpTmp->data);
                            delete pTmpTmp;
                            pTmpTmp = pTmpTmpN;
                        }
                        dataToAcked -= pTmp->len;
                        free(pTmp->data);
                        lastProcTsnRx = pTmp->tsn;
                        delete pTmp;
                        pTmp = pTmpNext;
                        pDataQueue = pTmp;
                        if(pDataQueue) pDataQueue->prev = NULL;
                        pBitStart = NULL;
                        totalLength = 0;
                        continue;
                    }
                    byte * sctpData = (byte *)malloc(totalLength);
                    if(sctpData) {
                        class RxSctpPackets *pTmpTmp = pBitStart;
                        int indexInnoData = 0;
                        while(pTmpTmp != pTmp) {
                            class RxSctpPackets *pTmpTmpN = pTmpTmp->next;
                            memcpy(&sctpData[indexInnoData], pTmpTmp->data, pTmpTmp->len); 
                            indexInnoData += pTmpTmp->len;
                            free(pTmpTmp->data);
                            delete pTmpTmp;
                            pTmpTmp = pTmpTmpN;
                        }
                        memcpy(&sctpData[indexInnoData], pTmp->data, pTmp->len); 
                        indexInnoData += pTmp->len;
                        if(indexInnoData != totalLength)  debug->printf("SCTP(%p)::GenerateSackMessageWithGaps ERROR!!!! %d!=%d", this, indexInnoData, totalLength);
                        if(pTmp->prot == 50) {
                            if(sctpData[0] == 0x3) ProcessWebrtcDcepMsg(sctpData, totalLength, pTmp->streamId);
                            else ProcessWebrtcDcepOkMsg(sctpData, totalLength);
                        }
                        else if(pTmp->prot == 53 || pTmp->prot == 52 || pTmp->prot == 51) {  // WebRTC Binary partial
                            SctpRecvApplicationData((char *)sctpData, totalLength);
                        }
                        else {
                            debug->printf("SCTP(%p)::GenerateSackMessageWithGaps no payload type recognized! %d", this, pTmp->prot);
                        }
                        free(sctpData);
                        free(pTmp->data);
                        lastProcTsnRx = pTmp->tsn;
                        delete pTmp;
                        pDataQueue = pTmpNext;
                        if (pDataQueue) pDataQueue->prev = NULL;
                        STRC_DATA(("SCTP(%p)::ProcessDataQueue ok2 pDataQueue=%p", this, pDataQueue));
                    }
                    dataToAcked -= totalLength;
                }
                pBitStart = NULL;
                totalLength = 0;
            }
        }
        else {
            break;
        }
        pTmp = pTmpNext;
    }
}

void SctpRtc::GenerateWebrtcDcepMessage(void)
{
    if(breakHandshake) return;

    int len = 12 + 16 + 12;
    byte * data = (byte *)malloc(len * sizeof(byte));
    if(data) {
        word tSeqNumber = seqNumber++;
        WRITE_WORD_NET(&data[0], srcSctpNumber);
        WRITE_WORD_NET(&data[2], dstSctpNumber);
        WRITE_DWORD_NET(&data[4], initDstTag);      // verification tag
        WRITE_DWORD_NET(&data[8], 0);               // checksum
        data[12] = 0;                               // Data Message
        data[13] = 0x3;                             // Flags
        WRITE_WORD_NET(&data[14], 16+12);           // Length
        WRITE_DWORD_NET(&data[16], initTsnSrc);     // Cumulative TSN ACK
        initTsnSrc++;
        WRITE_WORD_NET(&data[20], streamId);        // Stream identifier
        WRITE_WORD_NET(&data[22], tSeqNumber);      // Stream sequence number
        WRITE_DWORD_NET(&data[24], 50);             // Protocol Identifier, WebRTC DCEP
        // DATA_CHANNEL_OPEN Message
        data[28] = 0x3;                             // DATA_CHANNEL_OPEN                      
        data[29] = 0x81;                            // DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT_UNORDERED
        data[30] = data[31] = 0x0;                  // Priority
        WRITE_DWORD_NET(&data[32], 0x0);            // Reliability Parameter
        WRITE_WORD_NET(&data[36], 0x0);             // Label Length
        WRITE_WORD_NET(&data[38], 0x0);             // Protocol Length

        dword crc32 = generateCrc32c(data, len);
#if CRC_BIG_ENDIAN
        WRITE_DWORD_NET(&data[8], crc32);
#else
        WRITE_DWORD_LE(&data[8], crc32);
#endif
        SctpDtlsSend((char *)data, len);
        debug->printf("SCTP(%p)::GenerateWebrtcDcepMessage(%d) initTsnSrc=%u", this, len, initTsnSrc);
    }
}

void SctpRtc::GenerateWebrtcDcepOkMessage(void)
{
    int len = 12 + 16 + 4; 
    byte *data = (byte *)malloc(len);

    if(data) {
        word tSeqNumber = seqNumber++;
        WRITE_WORD_NET(&data[0], srcSctpNumber);
        WRITE_WORD_NET(&data[2], dstSctpNumber);
        WRITE_DWORD_NET(&data[4], initDstTag);      // verification tag
        WRITE_DWORD_NET(&data[8], 0);               // checksum
        data[12] = 0;                               // Data Message
        data[13] = 0x3;                             // Flags
        WRITE_WORD_NET(&data[14], 16+1);            // Length
        WRITE_DWORD_NET(&data[16], initTsnSrc);     // Cumulative TSN ACK
        initTsnSrc++;
        WRITE_WORD_NET(&data[20], streamId);        // Stream identifier
        WRITE_WORD_NET(&data[22], tSeqNumber);      // Stream sequence number
        WRITE_DWORD_NET(&data[24], 50);             // Protocol Identifier, WebRTC DCEP
        data[28] = 0x2;                             // DATA_CHANNEL_ACK 
        data[29] = data[30] = data[31] = 0x0;
        dword crc32 = generateCrc32c(data, 32);
#if CRC_BIG_ENDIAN
        WRITE_DWORD_NET(&data[8], crc32);
#else
        WRITE_DWORD_LE(&data[8], crc32);
#endif
        SctpDtlsSend((char *)data, len);
        debug->printf("SCTP(%p)::GenerateWebrtcDcepOkMessage(%d) initTsnSrc=%d SeqNumber=%d", this, len, initTsnSrc, tSeqNumber);
        this->user->SctpRtcConnected(false);
    }
}

void SctpRtc::GenerateForwardTsn(unsigned short lastSeq, unsigned int startTsn, unsigned int lastTsnAcked)
{
    int len = 12 + 4 + 4 + (2 + 2);
    byte *data = (byte *)malloc(len);

    WRITE_WORD_NET(&data[0], srcSctpNumber);
    WRITE_WORD_NET(&data[2], dstSctpNumber);
    WRITE_DWORD_NET(&data[4], initDstTag);                        // verification tag
    WRITE_DWORD_NET(&data[8], 0);                                 // checksum
    data[12] = 192;                                               // Forward TSN Message
    data[13] = 0x0;                                               // Flags
    WRITE_WORD_NET(&data[14], 12);                                // Length without seq_num
    WRITE_DWORD_NET(&data[16], lastTsnAcked);                     // Cumulative TSN ACK

    WRITE_WORD_NET(&data[20], streamId);           // stream id
    WRITE_WORD_NET(&data[22], lastSeq);            // stream num

    dword crc32 = generateCrc32c(data, len);
#if CRC_BIG_ENDIAN
    WRITE_DWORD_NET(&data[8], crc32);
#else
    WRITE_DWORD_LE(&data[8], crc32);
#endif
    SctpDtlsSend((char *)data, len);

    //debug->printf("SCTP(%p)::GenerateForwardTsn(%d) lastSeq=%u startTsn=%u newTsn=%u lastTsnSent=%u", this, len, lastSeq, startTsn, lastTsnAcked, initTsnSrc);

    //STRC_DATA(("SCTP Send Forward TSN (%x,%d) %u", this, len, new_tsn));
    //STRC_WINDOW(("SCTP Send Forward TSN (%x,%d) seq=%d tsn=%u %u", this, len, t_seq, new_tsn, init_tsn_src));
}

int SctpRtc::ProcessAckHeartbeat(byte *pData, dword len)
{
    word hblen = READ_WORD_NET(&pData[2]);
    if(hblen > 4) {
        byte *heartbeatInfo = (byte *)malloc((hblen-4)*sizeof(byte));
        if(heartbeatInfo) {
            // we should compare with the number random sent, ToBeDone
            free(heartbeatInfo);
        }
    }
    int padding = ((hblen + 4) % 4);
    if(padding) padding = 4 - padding;
    return hblen+padding;
}

int SctpRtc::ProcessHeartbeat(byte *pData, dword len)
{
    word hblen = READ_WORD_NET(&pData[2]);
    if(hblen > 4) {
        byte *heartbeatInfo = (byte *)malloc((hblen-4)*sizeof(byte));
        if(heartbeatInfo) {
            memcpy(heartbeatInfo, &pData[4], (hblen-4));
            // Do Something with it!
            GenerateHeartbeatAckMessage(heartbeatInfo, (hblen-4));
            free(heartbeatInfo);
        }
    }
    int padding = ((hblen + 4) % 4);
    if(padding) padding = 4 - padding;
    return hblen+padding;
}

int SctpRtc::ProcessError(byte *pData, int len)
{
    word causeLen = 0;
    int indexData = 4;
    word elen = READ_WORD_NET(&pData[2]);
    int totLength = elen;
    while(elen > 4) {
        word causeCode = READ_WORD_NET(&pData[indexData]);
        causeLen = READ_WORD_NET(&pData[indexData+2]) - 4;
        byte *abortReason = (byte *)malloc((causeLen+1)*sizeof(byte));
        if(abortReason) {
            memcpy(abortReason, &pData[indexData+4], causeLen);
            abortReason[causeLen] = 0;
            debug->printf("SCTP(%p)::Process Error(%d): %s", this, causeCode, abortReason);
            free(abortReason);
        }
        indexData += (4+causeLen);
        elen -= (4+causeLen);
    }
    int padding = ((totLength + 4) % 4);
    if(padding) padding = 4 - padding;
    return totLength+padding;
}

int SctpRtc::ProcessAbort(byte *pData, dword len)
{
    word causeLen = 0;

    word alen = READ_WORD_NET(&pData[2]);
    if(alen > 4) {
        word causeCode = READ_WORD_NET(&pData[4]);
        causeLen = READ_WORD_NET(&pData[6]) - 4;
        byte *abortReason = (byte *)malloc((causeLen+1)*sizeof(byte));
        if(abortReason) {
            memcpy(abortReason, &pData[8], causeLen);
            abortReason[causeLen] = 0;
            debug->printf("SCTP(%p)::ProcessAbort(%d): (%d) %s", this, causeCode, causeLen, abortReason);
            free(abortReason);
        }
    }
    int padding = ((alen + 4) % 4);
    if(padding) padding = 4 - padding;
    return alen+padding;
}

int SctpRtc::ProcessCookieEcho(byte *pData, dword len)
{
    word tCookieLen = READ_WORD_NET(&pData[2]) - 4;
    if(tCookieLen != txCookieLen) {
        debug->printf("SCTP(%p)::ProcessCookieEcho tCookieLen = %d txCookieLen = %d", this, tCookieLen, txCookieLen);
        return tCookieLen + 4;
    }
    if(txStateCookie) {
        if(memcmp(txStateCookie, &pData[4], tCookieLen)) {
            debug->printf("SCTP(%p)::ProcessCookieEcho Error, cookie does not match!", this);
            // generate_abort_message with cookie error!
        }
        else {
            GenerateCookieAckMessage();
        }
    }
    else {
        debug->printf("SCTP(%p)::ProcessCookieEcho Error, txStateCookie not set!", this);
    }
    int padding = ((tCookieLen + 4) % 4);
    if(padding) padding = 4 - padding;
    return tCookieLen + 4 + padding;
}

int SctpRtc::ProcessInitAck(byte * pData, int len)
{
    initDstTag = READ_DWORD_NET(&pData[4]);
    winDstCredit = READ_DWORD_NET(&pData[8]);
    initWinDstCredit = winDstCredit;
    inDstStreams = READ_WORD_NET(&pData[12]);
    outDstStreams = READ_WORD_NET(&pData[14]);
    initTsnDst = READ_DWORD_NET(&pData[16]);
    cumDstTsnAck = initTsnDst - 1;
    lastTsnRx = initTsnDst - 1;
    lastProcTsnRx = initTsnDst - 1;

    int remLength = len - 20;
    int indexB = 20;
    while(remLength > 0) {
        word ptype = READ_WORD_NET(&pData[indexB]);
        word plen = READ_WORD_NET(&pData[indexB + 2]) - 4;
        int padding = ((plen+16) % 4);
        if(padding) padding = 4 - padding;
        indexB += 4;
        remLength -= 4;
        switch(ptype) {
            case 0:  // Padding
                {
                    //trace("padding");
                    remLength -= (plen+padding);
                }
                break;
            case 7:  // State Cookie
                {
                    remLength -= (plen+padding);
                    debug->printf("SCTP(%p)::ProcessInitAck - State Cookie %d", this, plen);
                    if((rxStateCookie = (byte *)malloc(plen * sizeof(byte))) != NULL) {
                        rxCookieLen = plen;
                        memcpy(rxStateCookie, &pData[indexB], plen);
                        cookieEchoAckMessage = false;
                        GenerateCookieEchoMessage();
                    }
                    indexB += (plen+padding);
                }
                break;
            case 32768:  // ECN parameter
                {
                    debug->printf("SCTP(%p)::ProcessInitAck - ECN parameter", this);
                    remLength -= (plen+padding);
                    indexB += (plen+padding);
                }
                break;
            case 32770:  // Random
                {
                    remLength -= (plen+padding);
                    debug->printf("SCTP(%p)::ProcessInitAck - Random Parameter %d len=%d rem=%d pad=%d", this, ptype, plen, remLength, padding);
                    indexB += (plen+padding);
                }
                break;
            case 32771:  // Chunk List
                {
                    remLength -= (plen+padding);
                    debug->printf("SCTP(%p)::ProcessInitAck - Chunk List Parameter %d len=%d rem=%d pad=%d", this, ptype, plen, remLength, padding);
                    for(int i=0; i<plen; i++) {
                        debug->printf("SCTP(%p)::ProcessInitAck - Chunk type required to be authenticated %d", this, pData[indexB+i]);
                    }
                    indexB += (plen+padding);
                }
                break;
            case 32772:  // Requested HMAC Algorithm Parameter
                {
                    remLength -= (plen+padding);
                    debug->printf("SCTP(%p)::ProcessInitAck - Requested HMAC Algorithm Parameter %d len=%d rem=%d pad=%d", this, ptype, plen, remLength, padding);
                    indexB += (plen+padding);
                }
                break;
            case 32773:  // Padding Parameter
                {
                    remLength -= (plen+padding);
                    debug->printf("SCTP(%p)::ProcessInitAck - Padding Parameter %d len=%d rem=%d pad=%d", this, ptype, plen, remLength, padding);
                    indexB += (plen+padding);
                }
                break;
            case 32776:  // Supported Extensions Parameter
                {
                    remLength -= (plen+padding);
                    debug->printf("SCTP(%p)::ProcessInitAck - Supported Extensions Parameter %d len=%d rem=%d pad=%d", this, ptype, plen, remLength, padding);
                    for(int i=0; i<plen; i++) {
                        unsigned int opt = (unsigned int)pData[indexB+i];
                        switch(opt) {
                            case 15:
                                debug->printf("SCTP(%p)::ProcessInitAck - Authentication Chunk (AUTH) %d", this, pData[indexB+i]);
                                break;
                            case 128:
                                debug->printf("SCTP(%p)::ProcessInitAck - Address Configuration Acknowledgment (ASCONF-ACK) %d", this, pData[indexB+i]);
                                break;
                            case 130:
                                debug->printf("SCTP(%p)::ProcessInitAck - Re-configuration Chunk (RE-CONFIG) %d", this, pData[indexB+i]);
                                break;
                            case 192:
                                debug->printf("SCTP(%p)::ProcessInitAck - Forward TSN %d", this, pData[indexB+i]);
                                fwdTsnSupported = true;
                                break;
                            case 193:
                                debug->printf("SCTP(%p)::ProcessInitAck - Address Configuration Change Chunk (ASCONF) %d", this, pData[indexB+i]);
                                break;
                            default:
                                debug->printf("SCTP(%p)::ProcessInitAck - Supported Extensions Parameter %d,%d", this, pData[indexB+i], opt);
                                break;
                        }
                    }
                    indexB += (plen+padding);
                }
                break;
            case 49152:  // Forward TSN supported
                {
                    debug->printf("SCTP(%p)::ProcessInitAck Forward TSN supported", this);
                    remLength -= (plen+padding);
                    indexB += (plen+padding);
                }
                break;
            default:
                {
                    debug->printf("SCTP(%p)::ProcessInitAck Unknown parameter type(%d) %x", this, remLength, ptype);
                    remLength -= (plen+padding);
                    indexB += (plen+padding);
                    break;
                }
        }
    }
    return len;
}

// Unsupported parameters must be included in the INIT ACK message...
// https://tools.ietf.org/html/rfc4960#section-14.4
// 3.3.3.1.  Optional or Variable-Length Parameters
int SctpRtc::ProcessInit(byte *pData, dword len)
{        
    initDstTag = READ_DWORD_NET(&pData[4]);
    winDstCredit = (int)READ_DWORD_NET(&pData[8]);
    initWinDstCredit = winDstCredit;
    inDstStreams = READ_WORD_NET(&pData[12]);
    outDstStreams = READ_WORD_NET(&pData[14]);
    initTsnDst = READ_DWORD_NET(&pData[16]);
    cumDstTsnAck = initTsnDst - 1;
    lastProcTsnRx = initTsnDst - 1;

    debug->printf("SCTP(%p)::ProcessInit (%d) %x %d %d %d %u %u q=%d", this, len, initDstTag, winDstCredit, inDstStreams, outDstStreams, initTsnDst, cumDstTsnAck, pTxSctpQueue.empty());

    int remLength = len - 20;
    int indexB = 20;
    while (remLength >= 4) {
        word ptype = READ_WORD_NET(&pData[indexB]);
        word plen = READ_WORD_NET(&pData[indexB + 2]) - 4;
        int padding = ((plen+16) % 4);
        if(padding) padding = 4 - padding;
        indexB += 4;
        remLength -= 4;
        switch (ptype) {
            case 0:  // Padding
                {
                    remLength -= (plen+padding);
                    debug->printf("SCTP(%p)::ProcessInit Padding %d len=%d rem=%d pad=%d", this, ptype, plen, remLength, padding);
                    indexB += (plen+padding);
                }
                break;
            case 5:  // This parameter lists all the IPv4 addresses used at the sending endpoint.
                {
                    remLength -= (plen+padding);
                    debug->printf("SCTP(%p)::ProcessInit Sending endpoint Address %d len=%d rem=%d pad=%d", this, ptype, plen, remLength, padding);
                    indexB += (plen+padding);
                }
                break;
            case 6:  // This parameter lists all the IPv6 addresses used at the sending endpoint.
                {
                    remLength -= (plen+padding);
                    debug->printf("SCTP(%p)::ProcessInit IPv6 addresses %d len=%d rem=%d pad=%d", this, ptype, plen, remLength, padding);
                    indexB += (plen+padding);
                }
                break;
            case 9:  // This parameter provides a suggested life-span increment the receiver should add to its default cookie life-span (in milliseconds).
                {
                    remLength -= (plen+padding);
                    debug->printf("SCTP(%p)::ProcessInit life-span %d len=%d rem=%d pad=%d", this, ptype, plen, remLength, padding);
                    indexB += (plen+padding);
                }
                break;
            case 11:  // This parameter is a hostname as defined in RFC 1123, section 2.1
                {
                    remLength -= (plen+padding);
                    debug->printf("SCTP(%p)::ProcessInit hostname %d len=%d rem=%d pad=%d", this, ptype, plen, remLength, padding);
                    indexB += (plen+padding);
                }
                break;
            case 12:  // Supported Address Type
                {
                    remLength -= (plen+padding);
                    debug->printf("SCTP(%p)::ProcessInit Supported Address Type %d len=%d rem=%d pad=%d", this, ptype, plen, remLength, padding);
                    indexB += (plen+padding);
                }
                break;
            case 32768:  // ECN parameter
                {
                    remLength -= (plen+padding);
                    debug->printf("SCTP(%p)::ProcessInit ECN parameter %d len=%d rem=%d pad=%d", this, ptype, plen, remLength, padding);
                    indexB += (plen+padding);
                }
                break;
                // http://tools.ietf.org/html/rfc4895
            case 32770:  // Random
                {
                    remLength -= (plen+padding);
                    debug->printf("SCTP(%p)::ProcessInit Random Parameter %d len=%d rem=%d pad=%d", this, ptype, plen, remLength, padding);
                    indexB += (plen+padding);
                }
                break;
            case 32771:  // Chunk List
                {
                    remLength -= (plen+padding);
                    debug->printf("SCTP(%p)::ProcessInit Chunk List Parameter %d len=%d rem=%d pad=%d", this, ptype, plen, remLength, padding);
                    for(int i=0; i<plen; i++) {
                        debug->printf("SCTP(%p)::ProcessInit Chunk type required to be authenticated %d", this, pData[indexB+i]); 
                    }
                    indexB += (plen+padding);
                }
                break;
            case 32772:  // Requested HMAC Algorithm Parameter
                {
                    remLength -= (plen+padding);
                    debug->printf("SCTP(%p)::ProcessInit Requested HMAC Algorithm Parameter %d len=%d rem=%d pad=%d", this, ptype, plen, remLength, padding);
                    indexB += (plen+padding);
                }
                break;
            case 32773:  // Padding Parameter
                {
                    remLength -= (plen+padding);
                    debug->printf("SCTP(%p)::ProcessInit Padding Parameter %d len=%d rem=%d pad=%d", this, ptype, plen, remLength, padding);
                    indexB += (plen+padding);
                }
                break;
            case 32776:  // Supported Extensions Parameter
                {
                    remLength -= (plen+padding);
                    debug->printf("SCTP(%p)::ProcessInit Supported Extensions Parameter %d len=%d rem=%d pad=%d", this, ptype, plen, remLength, padding);
                    for(int i=0; i<plen; i++) {
                        switch(pData[indexB+i]) {
                            case 15:
                                debug->printf("SCTP(%p)::ProcessInit - Authentication Chunk (AUTH) %d", this, pData[indexB+i]); 
                                break;
                            case 128:
                                debug->printf("SCTP(%p)::ProcessInit - Address Configuration Acknowledgment (ASCONF-ACK) %d", this, pData[indexB+i]); 
                                break;
                            case 130:
                                debug->printf("SCTP(%p)::ProcessInit - Re-configuration Chunk (RE-CONFIG) %d", this, pData[indexB+i]); 
                                break;
                            case 192:
                                debug->printf("SCTP(%p)::ProcessInit - Forward TSN %d", this, pData[indexB+i]); 
                                fwdTsnSupported = true;
                                break;
                            case 193:
                                debug->printf("SCTP(%p)::ProcessInit - Address Configuration Change Chunk (ASCONF) %d", this, pData[indexB+i]); 
                                break;
                            default:
                                debug->printf("SCTP(%p)::ProcessInit - Supported Extensions Parameter %d", this, pData[indexB+i]); 
                                break;
                        }
                    }
                    indexB += (plen+padding);
                }
                break;
            case 49152:  // Forward TSN supported
                {
                    remLength -= (plen+padding);
                    debug->printf("SCTP(%p)::ProcessInit Forward TSN supported %d len=%d rem=%d pad=%d", this, ptype, plen, remLength, padding);
                    indexB += (plen+padding);
                }
                break;
            default:
                {
                    remLength -= (plen+padding);
                    debug->printf("SCTP(%p)::ProcessInit Unknown parameter type %d len=%d rem=%d pad=%d", this, ptype, plen, remLength, padding);
                    indexB += (plen+padding);
                }
                break;
        }
    }
    return len;
}

void SctpRtc::ProcessWebrtcDcepMsg(byte *pData, dword len, word streamId)
{
    debug->hexdump(pData, len);

    debug->printf("SCTP(%p)::ProcessWebrtcDcepMsg msg=%d ch=%d prio=%d reab=%d lb=%d prot=%d stream=%d", this,
          pData[0], pData[1], READ_WORD_NET(&pData[2]), READ_DWORD_NET(&pData[4]),
          READ_WORD_NET(&pData[8]), READ_WORD_NET(&pData[10]), streamId);
    this->streamId = streamId;
    dword labelLen = READ_WORD_NET(&pData[8]);
    if(labelLen && (labelLen <= (len-12))) {
        byte *labelCh = (byte *)malloc((labelLen+1)*sizeof(byte));
        if(labelCh) {
            memcpy(labelCh, &pData[12], labelLen);
            labelCh[labelLen] = 0;
            debug->printf("SCTP(%p)::ProcessWebrtcDcepMsg Label: %s", this, labelCh);
            free(labelCh);
        }
    }
    dword protLen = READ_WORD_NET(&pData[10]);
    if(protLen && (protLen <= (len-labelLen-12))) {
        byte *protCh = (byte *)malloc((protLen+1)*sizeof(byte));
        if(protCh) {
            memcpy(protCh, &pData[12+labelLen], protLen);
            protCh[protLen] = 0;
            debug->printf("SCTP(%p)::ProcessWebrtcDcepMsg Protocol: %s", this, protCh);
            free(protCh);
        }
    }
    GenerateWebrtcDcepOkMessage();
}

void SctpRtc::ProcessWebrtcDcepOkMsg(byte *pData, dword len)
{
    debug->printf("SCTP(%p)::ProcessWebrtcDcepOkMsg DCEP Ok len=%d msg=%d fwd=%d", this, len, pData[0], fwdTsnSupported);
    this->user->SctpRtcConnected(false);
}

int SctpRtc::ProcessData(byte *pData, dword len)
{
    int dataLen = READ_WORD_NET(&pData[2]);
    if(dataLen == 0) {
        return 4;
    }
    int padding = ((dataLen + 4) % 4);
    if(padding) padding = 4 - padding;
    dataLen -= 16;
    dword tsn = READ_DWORD_NET(&pData[4]);
    word streamId = READ_WORD_NET(&pData[8]);
    //word stream_seq_num = READ_WORD_NET(&pData[10]);
    dword ppid = READ_DWORD_NET(&pData[12]);

    //if (pData[1] & 0x3) debug->printf("SCTP process data(%x,%d) %d d=%d p=%d stream=%d payload=%d forward=%d tsn=%u cumDstTsnAck=%u lastProcTsnRx=%u queueTsn=%u", this, pData[1] & 0x3, len, dataLen, padding, streamId, ppid, fwdTsnSupported, tsn, cumDstTsnAck, lastProcTsnRx, pDataQueue ? pDataQueue->tsn : 0);

    if(dropMediaRx) {
        totalRxPackets++;
        if(dropSeveralRx || (--numAllowPacketsRx <= 0)) {
            //debug->printf("SctpRtc(%p)::ProcessData drop message len=%d tsn=%u numAllowPackets=%d dropSeveral=%d total=%d drop=%d perc=%d", this, len, tsn, numAllowPacketsRx, dropSeveralRx, totalRxPackets, dropRxPackets, (totalRxPackets * 100) / dropRxPackets);
            if(dropSeveralRx) dropSeveralRx--;
            else {
                dropSeveralRx = (IRandom::GetRandom() % 4) + 1;
                numAllowPacketsRx = (100 * dropSeveralRx) / dropMediaRx;
                //debug->printf("SctpRtc(%p)::ProcessData drop messages numAllowPackets=%d dropSeveral=%d total=%d drop=%d perc=%d", this, numAllowPacketsRx, dropSeveralRx, totalRxPackets, dropRxPackets, (dropRxPackets * 100) / totalRxPackets);
            }
            dropRxPackets++;
            return len;
        }
    }

    byte *data = (byte *)malloc(dataLen*sizeof(byte));
    if(!data) {
        return 16 + dataLen + padding;
    }
    memcpy(data, &pData[16], dataLen);
    class RxSctpPackets *pNew = new RxSctpPackets(data, dataLen, tsn, (pData[1] & 0x2 ? true : false), (pData[1] & 0x1 ? true : false), ppid, elapsedTimer, streamId);
    if(!pNew) {
        free(data);
        return 16 + dataLen + padding;
    }

    class RxSctpPackets *pTmp, *pTmp0;
    bool reorder = false;
    bool dupFound = false;

    pTmp = pTmp0 = pDataQueue;
    if(pNew->tsn <= lastProcTsnRx) {
        debug->printf("SCTP(%p)::ProcessData -- old packet!!! %u %u %u", pNew->tsn, lastProcTsnRx, cumDstTsnAck);
        dupFound = true;
        free(pNew->data);
        delete pNew;
    }
    else {
        while(pTmp != NULL) {
            if(pNew->tsn == pTmp->tsn) {
                pTmp->dup++;
                dupFound = true;
                free(pNew->data);
                delete pNew;
                break;
            }
            if(pNew->tsn < pTmp->tsn) {
                reorder = true;
                break; // pTmp0 points to previous packet and pTmp to the next
            }
            pTmp0 = pTmp;
            pTmp = pTmp->next;
        }
    }
    if(dupFound == false) {
        if(!pDataQueue) {
            pDataQueue = pNew;
        }
        else {
            if(reorder) {
                pNew->next = pTmp;
                pTmp->prev = pNew;   // pTmp != NULL
            }
            // in case of reorder and only one packet in the queue
            if(reorder && (pTmp == pTmp0))
                pDataQueue = pNew;
            else {
                pTmp0->next = pNew;
                pNew->prev = pTmp0;
            }
        }
        dataToAcked += pNew->len;

#if 1
        GenerateSackMessageWithGaps();
#else
        if(!fwdTsnSupported) {
            GenerateSackMessageWithGaps();
        }
        else {
            GenerateSackMessage(tsn);
            cumDstTsnAck = tsn;
            ProcessDataQueue();
        }
#endif
    }
    return 16 + dataLen + padding;
}

int SctpRtc::ProcessForwardTsn(byte *pData, int len)
{
    unsigned int cumTsnAck = READ_DWORD_NET(&pData[4]);

    if(cumTsnAck <= cumDstTsnAck) return len;

    if(forwardGapTsn != cumTsnAck) {
        log->Log(LOG_APPSHARING, "SCTP(%p)::ProcessForwardTsn len=%d tsn=%u cumDstTsnAck=%u lastProcTsnRx=%u qTsn=%u", this, len, cumTsnAck, cumDstTsnAck, lastProcTsnRx, pDataQueue ? pDataQueue->tsn : 0);
    }
    forwardGapTsn = cumTsnAck;
    foundGapTsn = 0;

    for(int i=8; i<len; i+=4) {
        STRC_DATA(("SCTP(%p)::ProcessForwardTsn Id=%d Seq=%d", this, READ_WORD_NET(&pData[i]), READ_WORD_NET(&pData[i + 2])));
    }

    if(fwdTsnSupported) {
        class RxSctpPackets *pTmp = pDataQueue;
        while(pTmp && pTmp->tsn <= cumTsnAck) {
            pTmp->acked = true;
            pTmp = pTmp->next;
        }
        cumDstTsnAck = cumTsnAck;
        lastProcTsnRx = cumTsnAck;
        GenerateSackMessageWithGaps();
    }
    return len;
}

void SctpRtc::MarkLostTsnSequence(unsigned int tsn)
{
    if(!sctpNumSeqTsns.empty()) {
        class SctpNumSeqTsn * s = sctpNumSeqTsns.front();
        while(s) {
            if(s->start <= tsn && s->end >= tsn) {
                STRC_DATA(("SctpRtc(%p)::MarkLostTsnSequence mark lost(%u) (s=%u -> e=%u) seq=%u num=(%x)", this, tsn, s->start, s->end, s->seq, s->num));
#ifdef MODULE_TEST
                char num[32];
                _sprintf(num, "%d", s->num);
                TEST_WATCH_CONTENT("_sctp_lost_num", num);
#endif
                s->remove();
                localWinDstCredit -= s->dataLength;
                delete s;
                return;
            }
            else if(tsn < s->start) {
#ifdef MODULE_TEST
                TEST_WATCH("_sctp_lost_old");
#endif
                break;
            }
            s = s->goNext();
        }
    }
}

void SctpRtc::AckTsnSequence(unsigned int tsn)
{
    if(!sctpNumSeqTsns.empty()) {
        class SctpNumSeqTsn * s = sctpNumSeqTsns.front();
        while(s) {
            class SctpNumSeqTsn * n = s->goNext();
            if(s->start <= tsn && s->end >= tsn) {
                int ackCount = 1;
                bool dup = false;
                if(!s->rxTsns.empty()) {
                    class SctpTxTsn * qTsn;
                    for(qTsn = s->rxTsns.front(); qTsn; qTsn = qTsn->goNext()) {
                        if(qTsn->tsn == tsn) {
                            //debug->printf("SctpRtc(%p)::AckTsnSequence received duplicated %u == %u (%u -> %u) ackCount=%d expected=%d", this, qTsn->tsn, tsn, s->start, s->end, ackCount, s->count);
                            dup = true;
                        }
                        else ackCount++;
                    }
                }
                if(s->count == ackCount) {
                    //if(s->num != 0x8000000) debug->printf("SctpRtc(%p)::AckTsnSequence packet received %u -> %u num=%x", this, s->start, s->end, s->num);
#ifdef MODULE_TEST
                    char num[32];
                    _sprintf(num, "%d", s->num);
                    TEST_WATCH_CONTENT("_sctp_recv_ack", num);
#endif
                    SctpRecvAck(s->num);
                    localWinDstCredit -= s->dataLength;
                    s->remove();
                    delete s;
                }
                else if(dup == false) {
                    //debug->printf("SctpRtc(%p)::AckTsnSequence some acks still missing %u (%u -> %u) %d < %d num=%x", this, tsn, s->start, s->end, ackCount, s->count, s->num);
                    s->rxTsns.push_back(new class SctpTxTsn(tsn));
                }
            }
            else if(tsn < s->start) {
#ifdef MODULE_TEST
                TEST_WATCH("_sctp_recv_old");
#endif
                break;
            }
            s = n;
        }
    }
}

int SctpRtc::ProcessSack(byte *pData, dword len)
{
    unsigned int cumTsnAck = READ_DWORD_NET(&pData[4]);
    winDstCredit = (int)READ_DWORD_NET(&pData[8]);
    word numberGapAcked = READ_WORD_NET(&pData[12]);
    word numberDupTsns = READ_WORD_NET(&pData[14]);
    tempWinDstCredit = winDstCredit;

    STRC_DATA(("SACK message(%x,%d,%d,f=%u,e=%u) (lastSeq=%u last=%u ack=%u < sent=%u) %d-%d-%d %u %u f=%d", this, handlingDone, sctpNumSeqTsns.empty(), sctpNumSeqTsns.empty() ? 0 : sctpNumSeqTsns.front()->start, sctpNumSeqTsns.empty() ? 0 : sctpNumSeqTsns.front()->end, lastSeqSack, lastTsnSrcAcked, cumTsnAck, initTsnSrc - 1, initWinDstCredit, winDstCredit, localWinDstCredit, numberGapAcked, numberDupTsns, fwdTsnSupported));
    STRC_WINDOW(("SACK message(%x,%d,%d,f=%u,e=%u) (lastSeq=%u last=%u ack=%u < sent=%u) %d-%d-%d %u %u f=%d", this, handlingDone, sctpNumSeqTsns.empty(), sctpNumSeqTsns.empty() ? 0 : sctpNumSeqTsns.front()->start, sctpNumSeqTsns.empty() ? 0 : sctpNumSeqTsns.front()->end, lastSeqSack, lastTsnSrcAcked, cumTsnAck, initTsnSrc - 1, initWinDstCredit, winDstCredit, localWinDstCredit, numberGapAcked, numberDupTsns, fwdTsnSupported));

    while(lastTsnSrcAcked < cumTsnAck) {
        lastTsnSrcAcked++;
        AckTsnSequence(lastTsnSrcAcked);
    }
    dword indData = 16;
    if(numberGapAcked) {
        unsigned short gStart = 0;
        unsigned short gEnd = 0;
        unsigned short lastGapEnd = 0;
        unsigned int startForwardTsn = 0;
        unsigned short lastSequence = 0;
        for(int i = 0; i<numberGapAcked; i++) {
            gStart = READ_WORD_NET(&pData[indData]);
            gEnd = READ_WORD_NET(&pData[indData + 2]);

            log->Log(LOG_APPSHARING, "SctpRtc(%p)::ProcessSack GAP[%u] %u -> (%u - %u) startForwardTsn=%u lastGapEnd=%d indData=%d len=%d", this, i, cumTsnAck, cumTsnAck + gStart, cumTsnAck + gEnd, startForwardTsn, lastGapEnd, indData, len);

            if(gStart > 1 && gEnd >= gStart) {
                startForwardTsn = cumTsnAck + gEnd;
                log->Log(LOG_APPSHARING, "SctpRtc(%p)::ProcessSack GAP[%u] %u -> (%u - %u) startForwardTsn=%u", this, i, cumTsnAck, cumTsnAck + gStart, cumTsnAck + gEnd, startForwardTsn);
                for (unsigned int j = cumTsnAck + lastGapEnd + 1; j < cumTsnAck + gStart; j++) {
                    MarkLostTsnSequence(j);
                    lastSequence = sequenceTable[j & 0xfff];
                }
                for (unsigned int j = cumTsnAck + gStart; j <= cumTsnAck + gEnd; j++) AckTsnSequence(j);
            }
            else break;  

            indData += 4;
            lastGapEnd = gEnd;
            if(indData >= len) break;
        }
        if(startForwardTsn && fwdTsnSupported) {
            /*This chunk shall be used by the data sender to inform the data
            receiver to adjust its cumulative received TSN point forward because
            some missing TSNs are associated with data chunks that SHOULD NOT be
            transmitted or retransmitted by the sender.*/
            GenerateForwardTsn(lastSequence, startForwardTsn, startForwardTsn);
        }
    }
    // indData must be incremented!!!
    if(numberDupTsns) {
        for(int i=0; i<numberDupTsns; i++) {
            //unsigned int dup_tsn = READ_DWORD_NET(&pData[ind_data]);
            //STRC_DATA(("Duplicated TSN %u", dup_tsn));
            indData += 4;
        }
    }

    // Remove packets from the send queue 
    class SctpMsg * qData = pTxSctpQueue.front();
    while(qData) {
        class SctpMsg * qNext = qData->goNext();
        unsigned int tTsn = (dword)READ_DWORD_NET(&qData->msg[16]);
        if(tTsn <= lastTsnSrcAcked) {
            localWinDstCredit -= qData->len;
            qData->remove();
            delete qData;
        }
        qData = qNext;
    }

    return indData;
}

bool SctpRtc::windowZeroProbe(word streamSeq, unsigned int lastAck)
{
    bool ret = false;
    class SctpMsg * data = pTxSctpQueue.front();
    unsigned short tsnSeq = 0;
    bool tsnSeqSet = false;
    bool haveSent = false;
    // last tsn acknowlegde, we start at the front of the queue
    while(data) {
        unsigned int tTsn = READ_DWORD_NET(&data->msg[16]);
        unsigned short tSeq = READ_WORD_NET(&data->msg[22]);
        if(tTsn == lastAck || (tsnSeqSet == true && tsnSeq == tSeq)) {
            if(tTsn == lastAck) {
                tsnSeq = tSeq;
                tsnSeqSet = true;
            }
            debug->printf("SCTP(%p)::windowZeroProbe  %u,%u -> (%u,%u,%u) l=%d", this, tTsn, lastAck, tsnSeq, tSeq, streamSeq, READ_WORD_NET(&data->msg[14]));
            char * rMsg = (char *)malloc(data->len);
            memcpy(rMsg, data->msg, data->len);
            SctpDtlsSend(rMsg, data->len);
            ret = true;
            haveSent = true;
        }
        else if(haveSent) break;
        data = data->goNext();
    }
    if(tsnSeq == streamSeq) return ret;

    // last sequence sent, probably at the end of the queue
    data = pTxSctpQueue.back();
    haveSent = false;
    istd::list<class SctpMsg> pResend;
    while(data) {
        unsigned short tSeq = READ_WORD_NET(&data->msg[22]);
        if(tSeq == streamSeq) {
            class SctpMsg * sctpMsg = new class SctpMsg(data->msg, data->len); 
            pResend.push_front(sctpMsg);
            ret = true;
            haveSent = true;
        }
        else if(haveSent) break;
        data = data->goPrev();
    }
    
    // Send only first and last packets ordered
    if(pResend.empty() == false) {
        data = pResend.front();
        if(data) {
            //unsigned int tTsn = READ_DWORD_NET(&data->msg[16]);
            //unsigned short tSeq = READ_WORD_NET(&data->msg[22]);
            char * rMsg = (char *)malloc(data->len);
            memcpy(rMsg, data->msg, data->len);
            SctpDtlsSend(rMsg, data->len);
            data->remove();
            delete data;
            if(pResend.empty() == false) {
                data = pResend.back();
                if(data) {
                    //unsigned int tTsn = READ_DWORD_NET(&data->msg[16]);
                    //unsigned short tSeq = READ_WORD_NET(&data->msg[22]);
                    char * rMsg = (char *)malloc(data->len);
                    memcpy(rMsg, data->msg, data->len);
                    SctpDtlsSend(rMsg, data->len);
                    data->remove();
                    delete data;
                }
                if (pResend.empty() == false) {
                    while (pResend.front()) delete pResend.front();
                }
            }
        }
    }
    return ret;
}

void SctpRtc::resendPacket(unsigned int sTsn, unsigned int eTsn)
{
    if(pTxSctpQueue.empty()) return;

    class SctpMsg * data = pTxSctpQueue.front();
    while(data) {
        unsigned int tTsn = (dword)READ_DWORD_NET(&data->msg[16]);
        if(tTsn >= sTsn && tTsn <= eTsn) {
            // check if t_tsn is in the retransmitted queue...if yes, stop, otherwise,
            if(findRetrasmittedTsn(tTsn) == true) {
                data = data->goNext();
                continue;
            }

            class SctpRetransmissions *pNew = new class SctpRetransmissions(tTsn, elapsedTimer);
            debug->printf("SCTP(%p)::resendPacket %u -> (%u - %u)", this, tTsn, sTsn, eTsn);
            char * rMsg = (char *)malloc(data->len);
            memcpy(rMsg, data->msg, data->len);
            SctpDtlsSend(rMsg, data->len);
            pRetrQueue.push_back(pNew);
        }
#if 0  // unordered
        else if(tTsn > eTsn) {
            //trace("resend_packet break!");
            break;
        }
#endif
        data = data->goNext();
    }
}

bool SctpRtc::findRetrasmittedTsn(unsigned int sTsn)
{
    if(pRetrQueue.empty()) return false;

    class SctpRetransmissions * pTmp = pRetrQueue.front();
    while(pTmp) {
        if(pTmp->tsn == sTsn) {
            //printf("diff time(%u) %u - %u\n", s_tsn, elapsed_timer, pTmp->timer_ticks); fflush(stdout);
            if((elapsedTimer - pTmp->timerTicks) > 4) {
                pTmp->remove();
                delete pTmp;

                return false;
            }
            else {
                return true;
            }
        }
        pTmp = pTmp->goNext();
    }
    return false;
}

void SctpRtc::clearSctpData(void)
{
    if(this->handlingDone == true) {
        if (txStateCookie) free(txStateCookie);
        txStateCookie = NULL;
        if(rxStateCookie) free(rxStateCookie);
        rxStateCookie = NULL;
    }
    this->handlingDone = false;
    this->isPeerAlive = false;
    this->breakHandshake = false;

    debug->printf("SCTP(%p)::clearSctpData q=%d q_t=%x tsn=%u,%u data_to_acked=%d", this, pTxSctpQueue.empty(), pDataQueue, initTsnSrc, lastTsnSrcAcked, dataToAcked);

    ProcessDataQueue();

    class RxSctpPackets *pRxSctp = pDataQueue;
    while(pRxSctp) {
        class RxSctpPackets *pSctp_ = pRxSctp;
        pRxSctp = pRxSctp->next;
        if(pSctp_->data) free(pSctp_->data);
        delete pSctp_;
    }
    pDataQueue = NULL;
    this->dataToAcked = 0;

    if(!pRetrQueue.empty()) {
        while(pRetrQueue.front()) delete pRetrQueue.front();
    }
    this->elapsedTimer = 0;

    this->seqNumber = 0;
    this->streamId = 0;
    this->fwdTsnSupported = false;
}

void SctpRtc::randBytes(unsigned char *dst, dword length)
{
    IRandom::GetBytes(dst, length);
    /*
    dword i;
    for (i = 0; i<length; i++) {
        dst[i] = (byte)((IRandom::GetRandom() * IRandom::GetRandom()) >> 1);
    }
    */
}

dword SctpRtc::generateCrc32c(unsigned char *buffer, dword length)
{
    static dword crc32c[256] =
    {
        0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L,
        0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL,
        0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL,
        0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L,
        0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL,
        0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L,
        0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L,
        0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL,
        0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL,
        0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L,
        0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L,
        0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL,
        0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L,
        0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL,
        0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL,
        0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L,
        0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L,
        0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L,
        0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L,
        0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L,
        0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L,
        0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L,
        0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L,
        0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L,
        0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L,
        0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L,
        0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L,
        0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L,
        0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L,
        0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L,
        0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L,
        0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L,
        0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL,
        0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L,
        0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L,
        0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL,
        0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L,
        0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL,
        0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL,
        0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L,
        0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L,
        0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL,
        0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL,
        0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L,
        0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL,
        0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L,
        0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L,
        0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL,
        0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L,
        0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL,
        0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL,
        0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L,
        0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL,
        0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L,
        0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L,
        0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL,
        0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL,
        0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L,
        0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L,
        0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL,
        0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L,
        0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL,
        0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL,
        0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L,
    };

    dword crc32 = ~0L;

#if CRC_BIG_ENDIAN
#define CRC32C(c, d)  ((c>>8) ^ crc32c[(c^(d)) & 0xFF])
    unsigned int i;
    for (i = 0; i < length; i++) {
        crc32 = CRC32C(crc32, buffer[i]);
    }

    unsigned int result = ~crc32;
    unsigned char byte0, byte1, byte2, byte3;

    byte0 = result & 0xff;
    byte1 = (result >> 8) & 0xff;
    byte2 = (result >> 16) & 0xff;
    byte3 = (result >> 24) & 0xff;
    crc32 = (((dword)byte0 << 24) | ((dword)byte1 << 16) | ((dword)byte2 << 8) | byte3);

    return crc32;
#else
    static dword crc32c1[256] =
    {
        0x00000000, 0x13a29877, 0x274530ee, 0x34e7a899,
        0x4e8a61dc, 0x5d28f9ab, 0x69cf5132, 0x7a6dc945,
        0x9d14c3b8, 0x8eb65bcf, 0xba51f356, 0xa9f36b21,
        0xd39ea264, 0xc03c3a13, 0xf4db928a, 0xe7790afd,
        0x3fc5f181, 0x2c6769f6, 0x1880c16f, 0xb225918,
        0x714f905d, 0x62ed082a, 0x560aa0b3, 0x45a838c4,
        0xa2d13239, 0xb173aa4e, 0x859402d7, 0x96369aa0,
        0xec5b53e5, 0xfff9cb92, 0xcb1e630b, 0xd8bcfb7c,
        0x7f8be302, 0x6c297b75, 0x58ced3ec, 0x4b6c4b9b,
        0x310182de, 0x22a31aa9, 0x1644b230, 0x5e62a47,
        0xe29f20ba, 0xf13db8cd, 0xc5da1054, 0xd6788823,
        0xac154166, 0xbfb7d911, 0x8b507188, 0x98f2e9ff,
        0x404e1283, 0x53ec8af4, 0x670b226d, 0x74a9ba1a,
        0xec4735f, 0x1d66eb28, 0x298143b1, 0x3a23dbc6,
        0xdd5ad13b, 0xcef8494c, 0xfa1fe1d5, 0xe9bd79a2,
        0x93d0b0e7, 0x80722890, 0xb4958009, 0xa737187e,
        0xff17c604, 0xecb55e73, 0xd852f6ea, 0xcbf06e9d,
        0xb19da7d8, 0xa23f3faf, 0x96d89736, 0x857a0f41,
        0x620305bc, 0x71a19dcb, 0x45463552, 0x56e4ad25,
        0x2c896460, 0x3f2bfc17, 0xbcc548e, 0x186eccf9,
        0xc0d23785, 0xd370aff2, 0xe797076b, 0xf4359f1c,
        0x8e585659, 0x9dface2e, 0xa91d66b7, 0xbabffec0,
        0x5dc6f43d, 0x4e646c4a, 0x7a83c4d3, 0x69215ca4,
        0x134c95e1, 0xee0d96, 0x3409a50f, 0x27ab3d78,
        0x809c2506, 0x933ebd71, 0xa7d915e8, 0xb47b8d9f,
        0xce1644da, 0xddb4dcad, 0xe9537434, 0xfaf1ec43,
        0x1d88e6be, 0xe2a7ec9, 0x3acdd650, 0x296f4e27,
        0x53028762, 0x40a01f15, 0x7447b78c, 0x67e52ffb,
        0xbf59d487, 0xacfb4cf0, 0x981ce469, 0x8bbe7c1e,
        0xf1d3b55b, 0xe2712d2c, 0xd69685b5, 0xc5341dc2,
        0x224d173f, 0x31ef8f48, 0x50827d1, 0x16aabfa6,
        0x6cc776e3, 0x7f65ee94, 0x4b82460d, 0x5820de7a,
        0xfbc3faf9, 0xe861628e, 0xdc86ca17, 0xcf245260,
        0xb5499b25, 0xa6eb0352, 0x920cabcb, 0x81ae33bc,
        0x66d73941, 0x7575a136, 0x419209af, 0x523091d8,
        0x285d589d, 0x3bffc0ea, 0xf186873, 0x1cbaf004,
        0xc4060b78, 0xd7a4930f, 0xe3433b96, 0xf0e1a3e1,
        0x8a8c6aa4, 0x992ef2d3, 0xadc95a4a, 0xbe6bc23d,
        0x5912c8c0, 0x4ab050b7, 0x7e57f82e, 0x6df56059,
        0x1798a91c, 0x43a316b, 0x30dd99f2, 0x237f0185,
        0x844819fb, 0x97ea818c, 0xa30d2915, 0xb0afb162,
        0xcac27827, 0xd960e050, 0xed8748c9, 0xfe25d0be,
        0x195cda43, 0xafe4234, 0x3e19eaad, 0x2dbb72da,
        0x57d6bb9f, 0x447423e8, 0x70938b71, 0x63311306,
        0xbb8de87a, 0xa82f700d, 0x9cc8d894, 0x8f6a40e3,
        0xf50789a6, 0xe6a511d1, 0xd242b948, 0xc1e0213f,
        0x26992bc2, 0x353bb3b5, 0x1dc1b2c, 0x127e835b,
        0x68134a1e, 0x7bb1d269, 0x4f567af0, 0x5cf4e287,
        0x4d43cfd, 0x1776a48a, 0x23910c13, 0x30339464,
        0x4a5e5d21, 0x59fcc556, 0x6d1b6dcf, 0x7eb9f5b8,
        0x99c0ff45, 0x8a626732, 0xbe85cfab, 0xad2757dc,
        0xd74a9e99, 0xc4e806ee, 0xf00fae77, 0xe3ad3600,
        0x3b11cd7c, 0x28b3550b, 0x1c54fd92, 0xff665e5,
        0x759baca0, 0x663934d7, 0x52de9c4e, 0x417c0439,
        0xa6050ec4, 0xb5a796b3, 0x81403e2a, 0x92e2a65d,
        0xe88f6f18, 0xfb2df76f, 0xcfca5ff6, 0xdc68c781,
        0x7b5fdfff, 0x68fd4788, 0x5c1aef11, 0x4fb87766,
        0x35d5be23, 0x26772654, 0x12908ecd, 0x13216ba,
        0xe64b1c47, 0xf5e98430, 0xc10e2ca9, 0xd2acb4de,
        0xa8c17d9b, 0xbb63e5ec, 0x8f844d75, 0x9c26d502,
        0x449a2e7e, 0x5738b609, 0x63df1e90, 0x707d86e7,
        0xa104fa2, 0x19b2d7d5, 0x2d557f4c, 0x3ef7e73b,
        0xd98eedc6, 0xca2c75b1, 0xfecbdd28, 0xed69455f,
        0x97048c1a, 0x84a6146d, 0xb041bcf4, 0xa3e32483
    };

    static dword crc32c2[256] =
    {
        0x00000000, 0xa541927e, 0x4f6f520d, 0xea2ec073,
        0x9edea41a, 0x3b9f3664, 0xd1b1f617, 0x74f06469,
        0x38513ec5, 0x9d10acbb, 0x773e6cc8, 0xd27ffeb6,
        0xa68f9adf, 0x3ce08a1, 0xe9e0c8d2, 0x4ca15aac,
        0x70a27d8a, 0xd5e3eff4, 0x3fcd2f87, 0x9a8cbdf9,
        0xee7cd990, 0x4b3d4bee, 0xa1138b9d, 0x45219e3,
        0x48f3434f, 0xedb2d131, 0x79c1142, 0xa2dd833c,
        0xd62de755, 0x736c752b, 0x9942b558, 0x3c032726,
        0xe144fb14, 0x4405696a, 0xae2ba919, 0xb6a3b67,
        0x7f9a5f0e, 0xdadbcd70, 0x30f50d03, 0x95b49f7d,
        0xd915c5d1, 0x7c5457af, 0x967a97dc, 0x333b05a2,
        0x47cb61cb, 0xe28af3b5, 0x8a433c6, 0xade5a1b8,
        0x91e6869e, 0x34a714e0, 0xde89d493, 0x7bc846ed,
        0xf382284, 0xaa79b0fa, 0x40577089, 0xe516e2f7,
        0xa9b7b85b, 0xcf62a25, 0xe6d8ea56, 0x43997828,
        0x37691c41, 0x92288e3f, 0x78064e4c, 0xdd47dc32,
        0xc76580d9, 0x622412a7, 0x880ad2d4, 0x2d4b40aa,
        0x59bb24c3, 0xfcfab6bd, 0x16d476ce, 0xb395e4b0,
        0xff34be1c, 0x5a752c62, 0xb05bec11, 0x151a7e6f,
        0x61ea1a06, 0xc4ab8878, 0x2e85480b, 0x8bc4da75,
        0xb7c7fd53, 0x12866f2d, 0xf8a8af5e, 0x5de93d20,
        0x29195949, 0x8c58cb37, 0x66760b44, 0xc337993a,
        0x8f96c396, 0x2ad751e8, 0xc0f9919b, 0x65b803e5,
        0x1148678c, 0xb409f5f2, 0x5e273581, 0xfb66a7ff,
        0x26217bcd, 0x8360e9b3, 0x694e29c0, 0xcc0fbbbe,
        0xb8ffdfd7, 0x1dbe4da9, 0xf7908dda, 0x52d11fa4,
        0x1e704508, 0xbb31d776, 0x511f1705, 0xf45e857b,
        0x80aee112, 0x25ef736c, 0xcfc1b31f, 0x6a802161,
        0x56830647, 0xf3c29439, 0x19ec544a, 0xbcadc634,
        0xc85da25d, 0x6d1c3023, 0x8732f050, 0x2273622e,
        0x6ed23882, 0xcb93aafc, 0x21bd6a8f, 0x84fcf8f1,
        0xf00c9c98, 0x554d0ee6, 0xbf63ce95, 0x1a225ceb,
        0x8b277743, 0x2e66e53d, 0xc448254e, 0x6109b730,
        0x15f9d359, 0xb0b84127, 0x5a968154, 0xffd7132a,
        0xb3764986, 0x1637dbf8, 0xfc191b8b, 0x595889f5,
        0x2da8ed9c, 0x88e97fe2, 0x62c7bf91, 0xc7862def,
        0xfb850ac9, 0x5ec498b7, 0xb4ea58c4, 0x11abcaba,
        0x655baed3, 0xc01a3cad, 0x2a34fcde, 0x8f756ea0,
        0xc3d4340c, 0x6695a672, 0x8cbb6601, 0x29faf47f,
        0x5d0a9016, 0xf84b0268, 0x1265c21b, 0xb7245065,
        0x6a638c57, 0xcf221e29, 0x250cde5a, 0x804d4c24,
        0xf4bd284d, 0x51fcba33, 0xbbd27a40, 0x1e93e83e,
        0x5232b292, 0xf77320ec, 0x1d5de09f, 0xb81c72e1,
        0xccec1688, 0x69ad84f6, 0x83834485, 0x26c2d6fb,
        0x1ac1f1dd, 0xbf8063a3, 0x55aea3d0, 0xf0ef31ae,
        0x841f55c7, 0x215ec7b9, 0xcb7007ca, 0x6e3195b4,
        0x2290cf18, 0x87d15d66, 0x6dff9d15, 0xc8be0f6b,
        0xbc4e6b02, 0x190ff97c, 0xf321390f, 0x5660ab71,
        0x4c42f79a, 0xe90365e4, 0x32da597, 0xa66c37e9,
        0xd29c5380, 0x77ddc1fe, 0x9df3018d, 0x38b293f3,
        0x7413c95f, 0xd1525b21, 0x3b7c9b52, 0x9e3d092c,
        0xeacd6d45, 0x4f8cff3b, 0xa5a23f48, 0xe3ad36,
        0x3ce08a10, 0x99a1186e, 0x738fd81d, 0xd6ce4a63,
        0xa23e2e0a, 0x77fbc74, 0xed517c07, 0x4810ee79,
        0x4b1b4d5, 0xa1f026ab, 0x4bdee6d8, 0xee9f74a6,
        0x9a6f10cf, 0x3f2e82b1, 0xd50042c2, 0x7041d0bc,
        0xad060c8e, 0x8479ef0, 0xe2695e83, 0x4728ccfd,
        0x33d8a894, 0x96993aea, 0x7cb7fa99, 0xd9f668e7,
        0x9557324b, 0x3016a035, 0xda386046, 0x7f79f238,
        0xb899651, 0xaec8042f, 0x44e6c45c, 0xe1a75622,
        0xdda47104, 0x78e5e37a, 0x92cb2309, 0x378ab177,
        0x437ad51e, 0xe63b4760, 0xc158713, 0xa954156d,
        0xe5f54fc1, 0x40b4ddbf, 0xaa9a1dcc, 0xfdb8fb2,
        0x7b2bebdb, 0xde6a79a5, 0x3444b9d6, 0x91052ba8
    };

    static dword crc32c3[256] =
    {
        0x00000000, 0xdd45aab8, 0xbf672381, 0x62228939,
        0x7b2231f3, 0xa6679b4b, 0xc4451272, 0x1900b8ca,
        0xf64463e6, 0x2b01c95e, 0x49234067, 0x9466eadf,
        0x8d665215, 0x5023f8ad, 0x32017194, 0xef44db2c,
        0xe964b13d, 0x34211b85, 0x560392bc, 0x8b463804,
        0x924680ce, 0x4f032a76, 0x2d21a34f, 0xf06409f7,
        0x1f20d2db, 0xc2657863, 0xa047f15a, 0x7d025be2,
        0x6402e328, 0xb9474990, 0xdb65c0a9, 0x6206a11,
        0xd725148b, 0xa60be33, 0x6842370a, 0xb5079db2,
        0xac072578, 0x71428fc0, 0x136006f9, 0xce25ac41,
        0x2161776d, 0xfc24ddd5, 0x9e0654ec, 0x4343fe54,
        0x5a43469e, 0x8706ec26, 0xe524651f, 0x3861cfa7,
        0x3e41a5b6, 0xe3040f0e, 0x81268637, 0x5c632c8f,
        0x45639445, 0x98263efd, 0xfa04b7c4, 0x27411d7c,
        0xc805c650, 0x15406ce8, 0x7762e5d1, 0xaa274f69,
        0xb327f7a3, 0x6e625d1b, 0xc40d422, 0xd1057e9a,
        0xaba65fe7, 0x76e3f55f, 0x14c17c66, 0xc984d6de,
        0xd0846e14, 0xdc1c4ac, 0x6fe34d95, 0xb2a6e72d,
        0x5de23c01, 0x80a796b9, 0xe2851f80, 0x3fc0b538,
        0x26c00df2, 0xfb85a74a, 0x99a72e73, 0x44e284cb,
        0x42c2eeda, 0x9f874462, 0xfda5cd5b, 0x20e067e3,
        0x39e0df29, 0xe4a57591, 0x8687fca8, 0x5bc25610,
        0xb4868d3c, 0x69c32784, 0xbe1aebd, 0xd6a40405,
        0xcfa4bccf, 0x12e11677, 0x70c39f4e, 0xad8635f6,
        0x7c834b6c, 0xa1c6e1d4, 0xc3e468ed, 0x1ea1c255,
        0x7a17a9f, 0xdae4d027, 0xb8c6591e, 0x6583f3a6,
        0x8ac7288a, 0x57828232, 0x35a00b0b, 0xe8e5a1b3,
        0xf1e51979, 0x2ca0b3c1, 0x4e823af8, 0x93c79040,
        0x95e7fa51, 0x48a250e9, 0x2a80d9d0, 0xf7c57368,
        0xeec5cba2, 0x3380611a, 0x51a2e823, 0x8ce7429b,
        0x63a399b7, 0xbee6330f, 0xdcc4ba36, 0x181108e,
        0x1881a844, 0xc5c402fc, 0xa7e68bc5, 0x7aa3217d,
        0x52a0c93f, 0x8fe56387, 0xedc7eabe, 0x30824006,
        0x2982f8cc, 0xf4c75274, 0x96e5db4d, 0x4ba071f5,
        0xa4e4aad9, 0x79a10061, 0x1b838958, 0xc6c623e0,
        0xdfc69b2a, 0x2833192, 0x60a1b8ab, 0xbde41213,
        0xbbc47802, 0x6681d2ba, 0x4a35b83, 0xd9e6f13b,
        0xc0e649f1, 0x1da3e349, 0x7f816a70, 0xa2c4c0c8,
        0x4d801be4, 0x90c5b15c, 0xf2e73865, 0x2fa292dd,
        0x36a22a17, 0xebe780af, 0x89c50996, 0x5480a32e,
        0x8585ddb4, 0x58c0770c, 0x3ae2fe35, 0xe7a7548d,
        0xfea7ec47, 0x23e246ff, 0x41c0cfc6, 0x9c85657e,
        0x73c1be52, 0xae8414ea, 0xcca69dd3, 0x11e3376b,
        0x8e38fa1, 0xd5a62519, 0xb784ac20, 0x6ac10698,
        0x6ce16c89, 0xb1a4c631, 0xd3864f08, 0xec3e5b0,
        0x17c35d7a, 0xca86f7c2, 0xa8a47efb, 0x75e1d443,
        0x9aa50f6f, 0x47e0a5d7, 0x25c22cee, 0xf8878656,
        0xe1873e9c, 0x3cc29424, 0x5ee01d1d, 0x83a5b7a5,
        0xf90696d8, 0x24433c60, 0x4661b559, 0x9b241fe1,
        0x8224a72b, 0x5f610d93, 0x3d4384aa, 0xe0062e12,
        0xf42f53e, 0xd2075f86, 0xb025d6bf, 0x6d607c07,
        0x7460c4cd, 0xa9256e75, 0xcb07e74c, 0x16424df4,
        0x106227e5, 0xcd278d5d, 0xaf050464, 0x7240aedc,
        0x6b401616, 0xb605bcae, 0xd4273597, 0x9629f2f,
        0xe6264403, 0x3b63eebb, 0x59416782, 0x8404cd3a,
        0x9d0475f0, 0x4041df48, 0x22635671, 0xff26fcc9,
        0x2e238253, 0xf36628eb, 0x9144a1d2, 0x4c010b6a,
        0x5501b3a0, 0x88441918, 0xea669021, 0x37233a99,
        0xd867e1b5, 0x5224b0d, 0x6700c234, 0xba45688c,
        0xa345d046, 0x7e007afe, 0x1c22f3c7, 0xc167597f,
        0xc747336e, 0x1a0299d6, 0x782010ef, 0xa565ba57,
        0xbc65029d, 0x6120a825, 0x302211c, 0xde478ba4,
        0x31035088, 0xec46fa30, 0x8e647309, 0x5321d9b1,
        0x4a21617b, 0x9764cbc3, 0xf54642fa, 0x2803e842
    };

    unsigned int* current = (unsigned int *)buffer;
    crc32 = ~0L;
    while(length >= 4)
    {
        crc32 ^= *current++;
        crc32  = crc32c3[crc32 & 0xFF] ^ crc32c2[(crc32>>8) & 0xFF] ^ crc32c1[(crc32>>16) & 0xFF] ^ crc32c[crc32>>24];
        length -= 4;
    }

    const unsigned char* currentChar = (unsigned char*)current;
    while(length-- != 0) {
        crc32 = ((crc32>>8) ^ crc32c[(crc32 & 0xFF) ^ (*currentChar++)]);
    }

    return ~crc32;
#endif
}

bool SctpRtc::validateCrc32(unsigned char *buffer, dword length)
{
#if CRC_BIG_ENDIAN
    dword originalCrc32 = READ_DWORD_NET(&buffer[8]);
#else
    dword originalCrc32 = READ_DWORD_LE(&buffer[8]);
#endif
    buffer[8] = buffer[9] = buffer[10] = buffer[11] = 0;
    dword crc32 = generateCrc32c(buffer, length);

#if 0
    if(originalCrc32 != crc32) {
        debug->printf("verify crc %x != %x data(%d)=%x", originalCrc32, crc32, length-4, READ_DWORD_NET(&buffer[length-4]));
    }
#endif
    return ((originalCrc32 == crc32) ? true : false);
}
