/*
 * Copyright (c) 2018, Texas Instruments Incorporated
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * *  Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * *  Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * *  Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

var moduleName = "NDK";

var viewMap = [
    {name: "UDP", fxn: "getUDP", structName: "UDPStats"},
    {name: "TCP", fxn: "getTCP", structName: "TCPStats"},
    {name: "Sockets", fxn: "getSockets", structName: "SocketsInfo"},
    {name: "Interfaces", fxn: "getInterfaces", structName: "Interface"}
];

/* ------------------------------- UDP Stats ------------------------------- */

function UDPStats()
{
    this.Recv = 0;
    this.Sent = 0;
    this.Dropped = 0;
    this.NoPort = 0;
    this.NoMCPort = 0;
}

function getUDP()
{
    var rawStats = Program.fetchVariable("NDK_udps");
    var udpStats = new UDPStats();

    udpStats.Recv = rawStats.RcvTotal;
    udpStats.Sent = rawStats.SndTotal;
    udpStats.Dropped = rawStats.RcvFull;
    udpStats.NoPort = rawStats.RcvNoPort;
    udpStats.NoMCPort = rawStats.RcvNoPortB;

    return udpStats;
}

/* ------------------------------- TCP Stats ------------------------------- */

function TCPStats()
{
    this.Recv = 0;
    this.Sent = 0;
}

function getTCP()
{
    var rawStats = Program.fetchVariable("NDK_tcps");
    var tcpStats = new TCPStats();

    tcpStats.Recv = rawStats.RcvTotal;
    tcpStats.Sent = rawStats.SndTotal;

    return tcpStats;
}

/* ------------------------------- Sockets --------------------------------- */

function SocketsInfo()
{
    this.Address = null;
    this.Ctx = null;
    this.Protocol = null;
    this.RxBufSize = null;
    this.RxBufSizeUsed = null;
    this.TxBufSize = null;
    this.TxBufSizeUsed = null;
    this.LocalAddress = null;
    this.ForeignAddress = null;
    this.State = null;
}

function getSockets()
{
    var view = new Array();

    var socketTable = Program.fetchVariable("pSockList");
    fillSocketView(view, socketTable, false);

    /*
     * Program.fetchVariable will return an error if the variable is not found
     * and this causes this whole CROV view to not work. This try catch block
     * handles this error state gracefully,
     * because non-ipv6 apps will not have the variable "pSock6List"
     */
    try
    {
        var socket6Table = Program.fetchVariable("pSock6List");
    }
    catch(err)
    {
        return view;
    }
    fillSocketView(view, socket6Table, true);


    return view;
}

function fillSocketView(view, socketTable, ipv6)
{
    var protocols = [
        "None",
        "TCP",
        "UDP",
        "RAW",
        "RAWETH"
    ];
    var tcpState = [
        "CLOSED",
        "LISTEN",
        "SYN SENT",
        "SYN RECVD",
        "ESTABLISHED",
        "CLOSE WAIT",
        "FIN WAIT1",
        "CLOSING",
        "LAST ACK",
        "FIN WAIT2",
        "TIME WAIT"
    ];

    /* loop through each protocol */
    for (var i = 0; i < (socketTable.length); i++)
    {
        /*
         * raweth socks use a different struct, so flag if this is a raweth sock
         * or not
         */
        var rawethsock;
        if(i == (socketTable.length - 1))
        {
            rawethsock = true;
        }
        else
        {
            rawethsock = false;
        }

        var socketAddress = socketTable[i];

        /* loop through each socket for the protocol */
        while(socketAddress != 0x0)
        {
            var socketsInfo = new SocketsInfo();

            var SOCK;
            /* raweth sock */
            if(rawethsock)
            {
                SOCK = Program.fetchFromAddr(socketAddress, "SOCKRAWETH");
            }
            else
            {
                /* ipv6 sock */
                if(ipv6)
                {
                    SOCK = Program.fetchFromAddr(socketAddress, "SOCK6");
                }
                /* regular sock */
                else
                {
                    SOCK = Program.fetchFromAddr(socketAddress, "SOCK");
                }
            }

            socketsInfo.Address = socketAddress;
            socketsInfo.Ctx = SOCK.Ctx;
            socketsInfo.Protocol = protocols[i];

            /* ---------------- Local and Foreign Address -------------------*/

            if(rawethsock)
            {
                socketsInfo.LocalAddress = "NA";
                socketsInfo.ForeignAddress = "NA";
            }
            else if(ipv6)
            {
                /*
                 * Currently CROV cannot access the LIP or FIP structs in the
                 * SOCK6 struct correctly.
                 */
                socketsInfo.LocalAddress = "Not Supported";
                socketsInfo.ForeignAddress = "Not Supported";
            }
            else
            {
                socketsInfo.LocalAddress = addrToDots(SOCK.LIP, true);
                socketsInfo.ForeignAddress = addrToDots(SOCK.FIP, true);
            }

            /* ---------------------- RX/TX Buffer --------------------------*/

            var rxBufPtr = SOCK.hSBRx;
            if(rxBufPtr != 0x0)
            {
                var rxBuf = Program.fetchFromAddr(rxBufPtr, "SB");
                socketsInfo.RxBufSizeUsed = rxBuf.Max - rxBuf.Total;
                socketsInfo.RxBufSize = SOCK.RxBufSize;
            }
            else
            {
                socketsInfo.RxBufSizeUsed = "No Buffer";
                socketsInfo.RxBufSize = "No Buffer";
            }

            var txBufPtr = SOCK.hSBTx;
            if(txBufPtr != 0x0)
            {
                var txBuf = Program.fetchFromAddr(txBufPtr, "SB");
                socketsInfo.TxBufSizeUsed = txBuf.Max - txBuf.Total;
                socketsInfo.TxBufSize = SOCK.TxBufSize;
            }
            else
            {
                socketsInfo.TxBufSizeUsed = "No Buffer";
                socketsInfo.TxBufSize = "No Buffer";
            }

            /* -------------------------- TCP Stats -------------------------*/

            var hTP;
            /* rawethsock does not have a hTP, so manually set it to 0 */
            if(rawethsock)
            {
                hTP = 0x0;
            }
            else
            {
                hTP = SOCK.hTP;
            }

            if(i == 1 && hTP != 0x0)
            {
                var TCPPROT = Program.fetchFromAddr(hTP, "TCPPROT");
                var t_state = TCPPROT.t_state
                if (t_state >= 0 && t_state <= 10) {
                    socketsInfo.State = tcpState[t_state];
                }
                else
                {
                    socketsInfo.State = "UNKOWN";
                }
            }
            else
            {
                socketsInfo.State = "NA - TCP Only";
            }

            view.push(socketsInfo);

            socketAddress = SOCK.pProtNext;
        }
    }
}

/* ---------------------------- Interfaces Info ---------------------------- */

function Interface()
{
    this.IF = 0;
    this.IPAddr = 0;
    this.IPMask = 0;
}

function getInterfaces()
{
    var ifs = new Array();
    var head = Program.lookupSymbolValue("pbindFirst");
    var next = Program.fetchFromAddr(head, "Ptr");
    while (next) {
        bind = Program.fetchFromAddr(next, "BIND");
        if (bind.Type == 0x9) {
            var nextif = new Interface();
            nextif.IF = bind.hIF;
            nextif.IPAddr = addrToDots(bind.IPHost, true);
            nextif.IPMask = addrToDots(bind.IPMask, true);
            ifs.push(nextif);
            next = bind.pNext;
        }
        else {
            next = 0;
        }
    }
    return ifs
}

function addrToDots(addr, little)
{
    if (little) {
        return (addr & 0xff) + "." + ((addr >> 8) & 0xff) + "." +
            ((addr >> 16) & 0xff) + "." + ((addr >> 24) & 0xff);
    }
    else {
        return (addr >> 24) + "." + ((addr >> 16) & 0xff) + "." +
            ((addr >> 8) & 0xff) + "." + (addr & 0xff);
    }
}
