// Copyright (C) 1999-2007 Open Source Telecom Corporation.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
// As a special exception, you may use this file as part of a free software
// library without restriction.  Specifically, if other files instantiate
// templates or use macros or inline functions from this file, or you compile
// this file and link it with other files to produce an executable, this
// file does not by itself cause the resulting executable to be covered by
// the GNU General Public License.  This exception does not however
// invalidate any other reasons why the executable file might be covered by
// the GNU General Public License.
//
// This exception applies only to the code released under the name GNU
// ccRTP.  If you copy code from other releases into a copy of GNU
// ccRTP, as the General Public License permits, the exception does
// not apply to the code that you add in this way.  To avoid misleading
// anyone as to the status of such modified files, you must delete
// this exception notice from them.
//
// If you write modifications of your own for GNU ccRTP, it is your choice
// whether to permit this exception to apply to your modifications.
// If you do not wish that, delete this exception notice.
//

/**
 * @file rtppkt.cpp
 * @short StaticPayloadFormat, DynamicPayloadFormat, RTPPacket,
 * OutgoingRTPPkt and IncomingRTPPkt classes implementation.
 **/

#include "private.h"
#include <ccrtp/rtppkt.h>
#include <ccrtp/CryptoContext.h>

#ifdef	CCXX_NAMESPACES
namespace ost {
#endif

// Default to 8Khz when no value is specified.
const uint32 PayloadFormat::defaultRTPClockRate = 8000;

//uint32 PayloadFormat::staticRates[lastStaticPayloadType]
uint32 StaticPayloadFormat::staticAudioTypesRates[] = {
	// audio types:
	8000,    //  0 - sptPCMU
	0,       //  1 - reserved
	8000,    //  2 - sptG726_32
	8000,    //  3 - sptGSM
	8000,    //  4 - sptG723
	8000,    //  5 - sptDVI4_8000
	16000,   //  6 - sptDVI4_16000
	8000,    //  7 - sptLPC
	8000,    //  8 - sptPCMA
	8000,    //  9 - sptG722
	44100,   // 10 - sptL16_DUAL
	44100,   // 11 - sptL16_MONO
	8000,    // 12 - sptQCELP
	0,       // 13 - reserved
	90000,   // 14 - sptMPA
	8000,    // 15 - sptG728
	11015,   // 16 - sptDVI4_11025
	22050,   // 17 - sptDVI4_22050
        8000     // 18 - sptG729
/*	0,       // reserved
	0,       // unassigned
	0,       // unassigned
	0,       // unassigned
	0        // unassigned
*/
	// All video types have 90000 hz RTP clock rate.
	// If sometime in the future a static video payload type is
	// defined with a different RTP clock rate (quite
	// unprobable). This table and/or the StaticPayloadType
	// constructor must be changed.
};

StaticPayloadFormat::StaticPayloadFormat(StaticPayloadType type)
{
	setPayloadType( (type <= lastStaticPayloadType)? type : 0);
	if ( type <= sptG729 ) {
		// audio static type
		setRTPClockRate(staticAudioTypesRates[type]);
	} else {
		// video static type
		setRTPClockRate(90000);
	}
}

DynamicPayloadFormat::DynamicPayloadFormat(PayloadType type, uint32 rate)
{
	PayloadFormat::setPayloadType(type);
	setRTPClockRate(rate);
}

// constructor commonly used for incoming packets
RTPPacket::RTPPacket(const unsigned char* const block, size_t len,
		     bool duplicate):
	total((uint32)len),
	duplicated(duplicate)
{

	const RTPFixedHeader* const header =
		reinterpret_cast<const RTPFixedHeader*>(block);
	hdrSize = sizeof(RTPFixedHeader) + (header->cc << 2);
	if ( header->extension ){
		RTPHeaderExt *ext = (RTPHeaderExt *)(block + hdrSize);
                hdrSize += sizeof(uint32) + (ntohs(ext->length) * 4);
	}
	if ( header->padding )
		len -= block[len - 1];
	payloadSize = (uint32)(len - hdrSize);

        if ( duplicate ) {
		buffer = new unsigned char[len];
		setbuffer(block,len,0);
	} else {
		buffer = const_cast<unsigned char*>(block);
	}
}

// constructor commonly used for outgoing packets
RTPPacket::RTPPacket(size_t hdrlen, size_t plen, uint8 paddinglen, CryptoContext* pcc ) :
	payloadSize((uint32)plen),
        buffer(NULL),
        hdrSize((uint32)hdrlen),
	duplicated(false)
{
	total = (uint32)(hdrlen + payloadSize);
	// compute if there must be padding
	uint8 padding = 0;
	if ( 0 != paddinglen ) {
		padding = paddinglen - (total % paddinglen);
		total += padding;
	}
        srtpLength = 0;
        srtpDataOffset = 0;
        if (pcc != NULL) {
                // compute additional memory for SRTP data
                srtpLength = pcc->getTagLength() + pcc->getMkiLength();
                srtpDataOffset = total; // SRTP data go behind header plus payload plus padding
        }

	// now we know the actual total length of the packet, get  some memory
        // but take SRTP data into account. Don't change total because some RTP
        // functions rely on the fact that total is the overall size (without
        // the SRTP data)
	buffer = new unsigned char[total + srtpLength];
	*(reinterpret_cast<uint32*>(getHeader())) = 0;
	getHeader()->version = CCRTP_VERSION;
	if ( 0 != padding ) {
		memset(buffer + total - padding,0,padding - 1);
		buffer[total - 1] = padding;
		getHeader()->padding = 1;
	} else {
		getHeader()->padding = 0;
	}
}

void
RTPPacket::endPacket()
{
#ifdef	CCXX_EXCEPTIONS
	try {
#endif
		delete [] buffer;
#ifdef	CCXX_EXCEPTIONS
	} catch (...) { };
#endif
}

OutgoingRTPPkt::OutgoingRTPPkt(
	const uint32* const csrcs, uint16 numcsrc,
        const unsigned char* const hdrext, uint32 hdrextlen,
	const unsigned char* const data, size_t datalen,
        uint8 paddinglen, CryptoContext* pcc) :
	RTPPacket((getSizeOfFixedHeader() + sizeof(uint32) * numcsrc
		  + hdrextlen),datalen,paddinglen, pcc)
{
	uint32 pointer = (uint32)getSizeOfFixedHeader();
	// add CSCR identifiers (putting them in network order).
	setCSRCArray(csrcs,numcsrc);
	pointer += numcsrc * sizeof(uint32);

	// add header extension.
	setbuffer(hdrext,hdrextlen,pointer);
	setExtension(hdrextlen > 0);
	pointer += hdrextlen;

	// add data.
        setbuffer(data,datalen,pointer);
}

OutgoingRTPPkt::OutgoingRTPPkt(
	const uint32* const csrcs, uint16 numcsrc,
	const unsigned char* data, size_t datalen,
        uint8 paddinglen, CryptoContext* pcc) :
	RTPPacket((getSizeOfFixedHeader() + sizeof(uint32) *numcsrc),datalen,
		  paddinglen, pcc)
{
	uint32 pointer = (uint32)getSizeOfFixedHeader();
	// add CSCR identifiers (putting them in network order).
	setCSRCArray(csrcs,numcsrc);
	pointer += numcsrc * sizeof(uint32);

	// not needed, as the RTPPacket constructor sets by default
	// the whole fixed header to 0.
	// getHeader()->extension = 0;

	// add data.
	setbuffer(data,datalen,pointer);
}

OutgoingRTPPkt::OutgoingRTPPkt(const unsigned char* data, size_t datalen,
			       uint8 paddinglen, CryptoContext* pcc) :
	RTPPacket(getSizeOfFixedHeader(),datalen,paddinglen, pcc)
{
	// not needed, as the RTPPacket constructor sets by default
	// the whole fixed header to 0.
	//getHeader()->cc = 0;
	//getHeader()->extension = 0;

	setbuffer(data,datalen,getSizeOfFixedHeader());
}

void
OutgoingRTPPkt::setCSRCArray(const uint32* const csrcs, uint16 numcsrc)
{
	setbuffer(csrcs, numcsrc * sizeof(uint32),getSizeOfFixedHeader());
	uint32* csrc = const_cast<uint32*>(getCSRCs());
	for ( int i = 0; i < numcsrc; i++ )
		csrc[i] = htonl(csrc[i]);
	getHeader()->cc = numcsrc;
}

void
OutgoingRTPPkt::protect(uint32 ssrc, CryptoContext* pcc)
{
        /* Encrypt the packet */
        uint64 index = ((uint64)pcc->getRoc() << 16) | (uint64)getSeqNum();

        pcc->srtpEncrypt(this, index, ssrc);

        // NO MKI support yet - here we assume MKI is zero. To build in MKI
        // take MKI length into account when storing the authentication tag.

        /* Compute MAC */
        pcc->srtpAuthenticate(this, pcc->getRoc(),
                                   const_cast<uint8*>(getRawPacket()+srtpDataOffset) );
        /* Update the ROC if necessary */
        if (getSeqNum() == 0xFFFF ) {
                pcc->setRoc(pcc->getRoc() + 1);
        }
}

// These masks are valid regardless of endianness.
const uint16 IncomingRTPPkt::RTP_INVALID_PT_MASK = (0x7e);
const uint16 IncomingRTPPkt::RTP_INVALID_PT_VALUE = (0x48);

IncomingRTPPkt::IncomingRTPPkt(const unsigned char* const block, size_t len) :
	RTPPacket(block,len)
{
	// first, perform validity check:
	// 1) check protocol version
	// 2) it is not an SR nor an RR
	// 3) consistent length field value (taking CC value and P and
	//    X bits into account)
	if ( getProtocolVersion() != CCRTP_VERSION
	     ||
             (getPayloadType() & RTP_INVALID_PT_MASK) == RTP_INVALID_PT_VALUE) {
            /*
	     ||
	     getPayloadSize() <= 0 ) {
            */
		headerValid = false;
		return;
	}
	headerValid = true;
	cachedTimestamp = getRawTimestamp();
	cachedSeqNum = ntohs(getHeader()->sequence);
	cachedSSRC = ntohl(getHeader()->sources[0]);
}

int32
IncomingRTPPkt::unprotect(CryptoContext* pcc)
{
        if (pcc == NULL) {
                return true;
        }

        /*
         * This is the setting of the packet data when we come to this
         * point:
         *
         * total:       complete length of received data
         * buffer:      points to data as received from network
         * hdrSize:     length of header including header extension
         * payloadSize: length of data excluding hdrSize and padding
         *
         * Because this is an SRTP packet we need to adjust some values here.
         * The SRTP MKI and authentication data is always at the end of a
         * packet. Thus compute the position of this data.
         */

        uint32 srtpDataIndex = total - (pcc->getTagLength() + pcc->getMkiLength());

        // now adjust total because some RTP functions rely on the fact that
        // total is the full length of data without SRTP data.
        total -= pcc->getTagLength() + pcc->getMkiLength();

        // recompute payloadSize by subtracting SRTP data
        payloadSize -= pcc->getTagLength() + pcc->getMkiLength();

	// unused??
        // const uint8* mki = getRawPacket() + srtpDataIndex;
        const uint8* tag = getRawPacket() + srtpDataIndex + pcc->getMkiLength();

        /* Replay control */
        if (!pcc->checkReplay(cachedSeqNum)) {
                return -2;
        }
        /* Guess the index */
        uint64 guessedIndex = pcc->guessIndex(cachedSeqNum);

        uint32 guessedRoc = guessedIndex >> 16;
        uint8* mac = new uint8[pcc->getTagLength()];

        pcc->srtpAuthenticate(this, guessedRoc, mac);
        if (memcmp(tag, mac, pcc->getTagLength()) != 0) {
            delete[] mac;
            return -1;
        }
        delete[] mac;

        /* Decrypt the content */
        pcc->srtpEncrypt( this, guessedIndex, cachedSSRC );

        /* Update the Crypto-context */
        pcc->update(cachedSeqNum);

        return 1;
}

#ifdef	CCXX_NAMESPACES
}
#endif

/** EMACS **
 * Local variables:
 * mode: c++
 * c-basic-offset: 8
 * End:
 */


syntax highlighted by Code2HTML, v. 0.9.1