/**************************************************************************** 
** File: rtcp.c
**
** Author: Mike Borella
**
** Comments: Dump RTCP header information. 
**
** $Id: rtcp.c,v 1.4 2007/06/25 12:07:39 farooq-i-azam Exp $
**
** 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 Library 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.
**
*****************************************************************************/

#include "rtcp.h"

/*
 * Static part of RTCP header
 *
 * 0                   1                   2                   3
 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |V=2|P|    RC   |       PT      |             length            | header
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *
 */

typedef struct rtcp_header
{
#if defined(WORDS_BIGENDIAN)
  u_int8_t version:2,
	   padding:1,
	   rc_sc:5;
#else
  u_int8_t rc_sc:5,
           padding:1,
           version:2;
#endif
  u_int8_t  packet_type;
  u_int16_t length;
} rtcp_header_t;


/*
 * RTCP SR packet type sender info portion
 *
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |                         SSRC of sender                        |
 * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
 * |              NTP timestamp, most significant word             | sender
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ info
 * |             NTP timestamp, least significant word             |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |                         RTP timestamp                         |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |                     sender's packet count                     |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |                      sender's octet count                     |
 * +---------------------------------------------------------------+
 */

typedef struct rtcp_sr_senderinfo
{
  u_int32_t sender_ssrc;
  u_int32_t timestamp_MSW;
  u_int32_t timestamp_LSW;
  u_int32_t timestamp_RTP;
  u_int32_t sender_pkt_cnt;
  u_int32_t sender_octet_cnt;
} rtcp_sr_senderinfo_t;

/*
 * RTCP SR report block
 *
 * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
 * |                 SSRC_1 (SSRC of first source)                 | 
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
 * | fraction lost |       cumulative number of packets lost       |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |           extended highest sequence number received           |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |                      interarrival jitter                      |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |                         last SR (LSR)                         |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |                   delay since last SR (DLSR)                  |
 * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
 */

typedef struct rtcp_sr_reportblock
{
  u_int32_t ssrc;
  u_int8_t  frac_lost;
  u_int8_t  packets_lost[3];
  u_int32_t ext_seqno_recvd;
  u_int32_t jitter;
  u_int32_t lsr;
  u_int32_t delay_since_lsr;
} rtcp_sr_reportblock_t;


/* 
 * RTCP packet type definitions 
 */

#define RTCP_PACKETTYPE_SR         200
#define RTCP_PACKETTYPE_RR         201
#define RTCP_PACKETTYPE_SDES       202
#define RTCP_PACKETTYPE_BYE        203
#define RTCP_PACKETTYPE_APP        204

/*
 * RTCP payload type map
 */

strmap_t rtcp_packettype_map[] =
{
  { RTCP_PACKETTYPE_SR,        "sender report" },
  { RTCP_PACKETTYPE_RR,        "receiver report" },
  { RTCP_PACKETTYPE_SDES,      "source description" },
  { RTCP_PACKETTYPE_BYE,       "bye" },
  { RTCP_PACKETTYPE_APP,       "application" },
  { 0, ""}
};

extern struct arg_t * my_args;

/*----------------------------------------------------------------------------
**
** dump_rtcp_sr()
**
** Parse RTCP sender report fields
**
**----------------------------------------------------------------------------
*/

void dump_rtcp_sr(packet_t * pkt, int count)
{
  rtcp_sr_senderinfo_t  senderinfo;
  rtcp_sr_reportblock_t reportblock;
  int reports_seen;
  
  /* Get the sender info */
  if (get_packet_bytes((u_int8_t *) &senderinfo, pkt, sizeof(senderinfo)) == 0)
    return;
  
  /* Conversions */
  senderinfo.sender_ssrc = ntohl(senderinfo.sender_ssrc);
  senderinfo.timestamp_MSW = ntohl(senderinfo.timestamp_MSW);
  senderinfo.timestamp_LSW = ntohl(senderinfo.timestamp_LSW);
  senderinfo.timestamp_RTP = ntohl(senderinfo.timestamp_RTP);
  senderinfo.sender_pkt_cnt = ntohl(senderinfo.sender_pkt_cnt);
  senderinfo.sender_octet_cnt = ntohl(senderinfo.sender_octet_cnt);
  
  /* Display */
  if (my_args->m)
    {
      display_minimal((u_int8_t *) &senderinfo.sender_ssrc, 4, DISP_DEC);
      display_minimal_string(" ");
    }
  else
    {
      display("Sender SSRC", (u_int8_t *) &senderinfo.sender_ssrc, 4, 
	      DISP_DEC);
      display("Timestamp MSW", (u_int8_t *) &senderinfo.timestamp_MSW, 4,
	      DISP_DEC);
      display("Timestamp LSW", (u_int8_t *) &senderinfo.timestamp_LSW, 4,
	      DISP_DEC);
      display("RTP timestamp", (u_int8_t *) &senderinfo.timestamp_RTP, 4,
	      DISP_DEC);
      display("Sender packet count", (u_int8_t *) &senderinfo.sender_pkt_cnt, 
	      4, DISP_DEC);
      display("Sender octet count", (u_int8_t *) &senderinfo.sender_octet_cnt,
	      4, DISP_DEC);
    }
  
  /* Loop over report blocks */
  reports_seen = 0;
  while(reports_seen < count)
    {
      /* Get the report block */
      if (get_packet_bytes((u_int8_t *) &reportblock, pkt, 
			   sizeof(reportblock)) == 0)
	break;
      
      /* Conversions */
      reportblock.ssrc = ntohl(reportblock.ssrc);
      reportblock.ext_seqno_recvd = ntohl(reportblock.ext_seqno_recvd);
      reportblock.jitter = ntohl(reportblock.jitter);
      reportblock.lsr = ntohl(reportblock.lsr);
      reportblock.delay_since_lsr = ntohl(reportblock.delay_since_lsr);

      reverse_byte_order ( ( u_int8_t * ) &reportblock.packets_lost, 3 );
      
      /* Display */
      if (my_args->m)
	{
	  display_minimal((u_int8_t *) &reportblock.ssrc, 4, DISP_DEC);
	  display_minimal_string(" ");
	}
      else
	{
	  display("SSRC", (u_int8_t *) &reportblock.ssrc, 4, DISP_DEC);
	  display("  Fraction lost", (u_int8_t *) &reportblock.frac_lost,
		  1, DISP_DEC);
	  display("  Packets lost", 
		  (u_int8_t *) &reportblock.packets_lost, 3, DISP_DEC);
	  display("  Highest seqno received", 
		  (u_int8_t *) &reportblock.ext_seqno_recvd, 4, 
		  DISP_DEC);
	  display("  Jitter", (u_int8_t *) &reportblock.jitter, 4, 
		  DISP_DEC);
	  display("  Last SR", (u_int8_t *) &reportblock.lsr, 4, 
		  DISP_DEC);
	  display("  Delay since last SR", 
		  (u_int8_t *) &reportblock.delay_since_lsr, 4,
		  DISP_DEC);
	}
      reports_seen ++;
    }
  
}

/*----------------------------------------------------------------------------
**
** dump_rtcp_rr()
**
** Parse RTCP receiver report fields
**
**----------------------------------------------------------------------------
*/

void dump_rtcp_rr(packet_t * pkt, int count)
{
  rtcp_sr_reportblock_t reportblock;
  int                   reports_seen;
  u_int32_t             ssrc;

  /* Get the SSRC */
  if (get_packet_bytes((u_int8_t *) &ssrc, pkt, 4) == 0)
    return;

  /* Conversions */
  ssrc = ntohl(ssrc);
  
  /* Display */
  if (my_args->m)
    {
      display_minimal((u_int8_t *) &ssrc, 4, DISP_DEC);
      display_minimal_string(" ");
    }
  else
    display("SSRC", (u_int8_t *) &ssrc, 4, DISP_DEC);
  
  /* Loop over report blocks */
  reports_seen = 0;
  while(reports_seen < count)
    {
      /* Get the report block */
      if (get_packet_bytes((u_int8_t *) &reportblock, pkt, 
			   sizeof(reportblock)) == 0)
	break;
      
      /* Conversions */
      reportblock.ssrc = ntohl(reportblock.ssrc);
      reportblock.ext_seqno_recvd = ntohl(reportblock.ext_seqno_recvd);
      reportblock.jitter = ntohl(reportblock.jitter);
      reportblock.lsr = ntohl(reportblock.lsr);
      reportblock.delay_since_lsr = ntohl(reportblock.delay_since_lsr);

      reverse_byte_order ( ( u_int8_t * ) &reportblock.packets_lost, 3 );
      
      /* Display */
      if (my_args->m)
	{
	  display_minimal((u_int8_t *) &reportblock.ssrc, 4, DISP_DEC);
	  display_minimal_string(" ");
	}
      else
	{
	  display("SSRC", (u_int8_t *) &reportblock.ssrc, 4, DISP_DEC);
	  display("  Fraction lost", (u_int8_t *) &reportblock.frac_lost,
		  1, DISP_DEC);
	  display("  Packets lost", 
		  (u_int8_t *) &reportblock.packets_lost, 3, DISP_DEC);
	  display("  Highest seqno received", 
		  (u_int8_t *) &reportblock.ext_seqno_recvd, 4, 
		  DISP_DEC);
	  display("  Jitter", (u_int8_t *) &reportblock.jitter, 4, 
		  DISP_DEC);
	  display("  Last SR", (u_int8_t *) &reportblock.lsr, 4, 
		  DISP_DEC);
	  display("  Delay since last SR", 
		  (u_int8_t *) &reportblock.delay_since_lsr, 4,
		  DISP_DEC);
	}
      reports_seen ++;
    }
  
}

/*----------------------------------------------------------------------------
**
** dump_rtcp_sdes()
**
** Parse RTCP source description fields
**
**----------------------------------------------------------------------------
*/

void dump_rtcp_sdes(packet_t * pkt, int count)
{
  u_int32_t  ssrc;
  u_int8_t   type;
  u_int8_t   length;
  u_int8_t * string;
  int        chunks_read;
  int        pad_len;

  chunks_read = 0;
  while(chunks_read < count)
    {
      /* Get the ssrc, type and length */
      if (get_packet_bytes((u_int8_t *) &ssrc, pkt, 4) == 0)
	break;

      /* Conversions */
      ssrc = ntohl(ssrc);

      /* Display */
      if (my_args->m)
	{
	  display_minimal((u_int8_t *) &ssrc, 4, DISP_DEC);
	  display_minimal_string(" ");	  
	}
      else
	display("SSRC/CSRC", (u_int8_t *) &ssrc, 4, DISP_DEC);
      
      /* Loop through items */
      while (1)
	{
	  u_int8_t byte;

	  if (get_packet_bytes((u_int8_t *) &type, pkt, 1) == 0)
	    break;
	  if (get_packet_bytes((u_int8_t *) &length, pkt, 1) == 0)
	    break;
	  
	  /* Allocate memory for the string then get it */
	  string = my_malloc(length+1);
	  if (get_packet_bytes(string, pkt, length) == 0)
	    break;
	  string[length] = '\0';
	  
	  /* Display */
	  if (my_args->m)
	    {
	      display_minimal_string(string);
	      display_minimal_string(" ");
	    }
	  else
	    {
	      display("  Type", (u_int8_t *) &type, 1, DISP_DEC);
	      display("  Length", (u_int8_t *) &length, 1, DISP_DEC);
	      display_string("  SDES", string);
	    }

	  /* Free string memory */
	  my_free(string);
	  
	  /* Look for a null terminator */
	  if (look_packet_bytes((u_int8_t *) &byte, pkt, 1) == 0)
	    break;
	  if (byte == 0)
	    break;
	}

      /* Figure out the pad and skip by it */
      pad_len = 4 - (length+2) % 4;
      if (skip_packet_bytes(pkt, pad_len) == 0)
	break;
      
      chunks_read ++;
    }
}

/*----------------------------------------------------------------------------
**
** dump_rtcp()
**
** Parse RTCP packet and dump fields
**
**----------------------------------------------------------------------------
*/

void dump_rtcp(packet_t * pkt)
{
  rtcp_header_t rtcp;
  u_int8_t      packet_type;
  u_int8_t      padding;
  u_int8_t      version;
  u_int8_t      count;
  u_int16_t     bytes_remaining;

  /* Set the layer */
  set_layer(LAYER_APPLICATION);

  while(1)
    {
      /* Get the fixed RTCP header */
      if (get_packet_bytes((u_int8_t *) &rtcp, pkt, sizeof(rtcp)) == 0)
	break;
  
      /* Conversions */
      packet_type = rtcp.packet_type;
      padding = rtcp.padding;
      version = rtcp.version;
      count = rtcp.rc_sc;
      rtcp.length = ntohs(rtcp.length);
      
      /* Set the number of bytes remaining */
      bytes_remaining = 4 * rtcp.length;
      
      /* Display */
      if (my_args->m)
	{
	  display_minimal_string("| RTCPv");
	  display_minimal((u_int8_t *) &version, 1, DISP_DEC);
	  display_minimal_string(" ");
	  display_minimal_string(map2str(rtcp_packettype_map, packet_type));
	  display_minimal_string(" ");
	}
      else
	{
	  display_header_banner("RTCP Header");
	  display("Version", (u_int8_t *) &version, 1, DISP_DEC);
	  display("Padding", (u_int8_t *) &padding, 1, DISP_DEC);
	  display("Report/source count", (u_int8_t *) &count, 1, DISP_DEC);
	  display_strmap("Packet type", packet_type, rtcp_packettype_map);
	  display("Length", (u_int8_t *) &rtcp.length, 2, DISP_DEC);
	}
      
      switch(packet_type)
	{
	case RTCP_PACKETTYPE_SR:
	  dump_rtcp_sr(pkt, count);
	  break;

	case RTCP_PACKETTYPE_RR:
	  dump_rtcp_rr(pkt, count);
	  break;

	case RTCP_PACKETTYPE_SDES:
	  dump_rtcp_sdes(pkt, count);
	  break;
     
	default:
	  break;
	}
    }

  /* Dump the hex buffer */
  hexbuffer_flush();
}
