/*## (c) SECURITY EXPLORATIONS    2018 poland                                #*/
/*##     http://www.security-explorations.com                                #*/

/* THIS SOFTWARE IS PROTECTED BY DOMESTIC AND INTERNATIONAL COPYRIGHT LAWS    */
/* UNAUTHORISED COPYING OF THIS SOFTWARE IN EITHER SOURCE OR BINARY FORM IS   */
/* EXPRESSLY FORBIDDEN. ANY USE, INCLUDING THE REPRODUCTION, MODIFICATION,    */
/* DISTRIBUTION, TRANSMISSION, RE-PUBLICATION, STORAGE OR DISPLAY OF ANY      */
/* PART OF THE SOFTWARE, FOR COMMERCIAL OR ANY OTHER PURPOSES REQUIRES A      */
/* VALID LICENSE FROM THE COPYRIGHT HOLDER.                                   */

/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS    */
/* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,*/
/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL    */
/* SECURITY EXPLORATIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
/* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF  */
/* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE     */
/* SOFTWARE.                                                                  */

import java.lang.*;
import java.util.*;
import javax.tv.service.*;
import org.davic.mpeg.TransportStream;
import org.davic.net.dvb.*;

public class MPEG {
 public static class ESInfo {
  int type;
  int pid;

  public ESInfo(int type,int pid) {
   this.type=type;
   this.pid=pid;
  }

  public int type() {
   return type;
  }

  public int pid() {
   return pid;
  }
 }

 public static final int PAD              = 20;
 public static final int PAD2             = 25;

 public static final String stream_types[]={
  "ITU-T | ISO/IEC Reserved",
  "ISO/IEC 11172-2 Video",
  "ITU-T Rec. H.262 | ISO/IEC 13818-2 Video or ISO/IEC 11172-2 constrained parameter video stream",
  "ISO/IEC 11172-3 Audio",
  "ISO/IEC 13818-3 Audio",
  "ITU-T Rec. H.222.0 | ISO/IEC 13818-1 private_sections",
  "ITU-T Rec. H.222.0 | ISO/IEC 13818-1 PES packets containing private data -- traditionally DVB Dolby (AC-3)",
  "ISO/IEC 13522 MHEG",
  "ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Annex A DSM CC",
  "ITU-T Rec. H.222.1",
  "ISO/IEC 13818-6 type A",
  "ISO/IEC 13818-6 type B",
  "ISO/IEC 13818-6 type C",
  "ISO/IEC 13818-6 type D",
  "ITU-T Rec. H.222.0 | ISO/IEC 13818-1 auxiliary",
  "ISO/IEC 13818-7 Audio with ADTS transport syntax",
  "ISO/IEC 14496-2 Visual",
  "ISO/IEC 14496-3 Audio with the LATM transport syntax as defined in ISO/IEC 14496-3 / AMD 1",
  "ISO/IEC 14496-1 SL-packetized stream or FlexMux stream carried in PES packets",
  "ISO/IEC 14496-1 SL-packetized stream or FlexMux stream carried in ISO/IEC14496_sections",
  "ISO/IEC 13818-6 Synchronized Download Protocol",
  "Metadata carried in PES packets",
  "Metadata carried in metadata_sections",
  "Metadata carried in ISO/IEC 13818-6 Data Carousel",
  "Metadata carried in ISO/IEC 13818-6 Object Carousel",
  "Metadata carried in ISO/IEC 13818-6 Synchronized Download Protocol",
  "IPMP stream (defined in ISO/IEC 13818-11, MPEG-2 IPMP)",
  "AVC video stream as defined in ITU-T Rec. H.264 | ISO/IEC 14496-10"
 };

 public static final String desc_types[]={
  "Reserved",
  "Reserved",
  "video_stream_descriptor",
  "audio_stream_descriptor",
  "hierarchy_descriptor",
  "registration_descriptor",
  "data_stream_alignment_descriptor",
  "target_background_grid_descriptor",
  "Video_window_descriptor",
  "CA_descriptor",
  "ISO_639_language_descriptor",
  "System_clock_descriptor",
  "Multiplex_buffer_utilization_descriptor",
  "Copyright_descriptor",
  "Maximum_bitrate_descriptor",
  "Private_data_indicator_descriptor",
  "Smoothing_buffer_descriptor",
  "STD_descriptor",
  "IBP_descriptor",
  "Defined in ISO/IEC 13818-6",
  "Defined in ISO/IEC 13818-6",
  "Defined in ISO/IEC 13818-6",
  "Defined in ISO/IEC 13818-6",
  "Defined in ISO/IEC 13818-6",
  "Defined in ISO/IEC 13818-6",
  "Defined in ISO/IEC 13818-6",
  "Defined in ISO/IEC 13818-6",
  "MPEG-4_video_descriptor",
  "MPEG-4_audio_descriptor",
  "IOD_descriptor",
  "SL_descriptor",
  "FMC_descriptor",
  "External_ES_ID_descriptor",
  "MuxCode_descriptor",
  "FmxBufferSize_descriptor",
  "MultiplexBuffer_descriptor"
 };

 public static int LINESIZE = 16;

 private static String allowed_chars="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890!@#$%^&*()_+-={}[]|:\";',.<>?/";

 private static String char_value(char c) {
  char tab[]=new char[1];

  if (allowed_chars.indexOf(c)!=-1) {
   tab[0]=c;
   return new String(tab);
  } else return ".";
 }

 private static void print_line(int pad,byte tab[],int pos) throws Throwable {
  String str=dopad(pad)+Utils.hex_value(pos,4)+": ";

  int size=tab.length-pos;
   
  if (size>LINESIZE) size=LINESIZE;

  for(int i=pos;i<(pos+size);i++) {
   str+=" "+Utils.hex_value((int)tab[i],2);
  }

  str+="  ";  

  for(int i=pos;i<(pos+size);i++) {
   str+=char_value((char)tab[i]);
  }

  Output.text(str);
 }

 public static void print_buf(int pad,byte tab[]) throws Throwable {
  int pos=0;
  
  while(pos<tab.length) {
   print_line(pad,tab,pos);
   pos+=LINESIZE;
  }
 }

 private static int read_short(byte data[],int pos) {
  return ((((int)data[pos])&0xff)<<8)|(((int)data[pos+1])&0xff);
 }

 private static int lookup_ca_pid(byte data[],int pos,int size,int sys_id) {
  int ca_pid=0;

  while(pos<size) {
   int tag=(int)data[pos];
   int len=(int)data[pos+1];

   if (tag==MPEGDefs.CA_DESCRIPTOR) {
    int ca_id=read_short(data,pos+2);

    if (ca_id==sys_id) {
     ca_pid=read_short(data,pos+4)&0x1fff;
     break;
    }
   }

   pos+=len+2;   
  }

  return ca_pid;
 }

 public static int get_emm_pid(int ca_sys_id) {
  byte cat[]=MPEGSniffer.get_CAT();

  int pos=MPEGDefs.SECTION_DATA_OFF;

  int size=cat.length-4;

  return lookup_ca_pid(cat,pos,size,ca_sys_id);
 }


 public static int get_PMT_pid(int svid) {
  int pmt_pid=0;

  try {
   byte pat[]=MPEGSniffer.get_PAT();

   int pos=MPEGDefs.SECTION_DATA_OFF;

   int size=pat.length-4;

   while(pos<size) {
    int prognum=read_short(pat,pos);
    int pid=read_short(pat,pos+2)&0x1fff;

    if (prognum==svid) {
     pmt_pid=pid;
     break;
    }

    pos+=4;  
   }

  } catch(Throwable t) {}

  return pmt_pid;
 }

 public static int get_ecm_pid(int svid) {
  int pmt_pid=get_PMT_pid(svid);

  if (pmt_pid>0) {
   byte pmt[]=MPEGSniffer.get_PMT(pmt_pid,svid);

   int pos=MPEGDefs.SECTION_DATA_OFF;

   int size=pmt.length-4;

   pos+=2;

   int pinfolen=read_short(pmt,pos)&0xfff; 

   int ca_pid=lookup_ca_pid(pmt,pos+2,pos+2+pinfolen,Conax.CA_ID);

   if (ca_pid>0) return ca_pid;

   pos+=2+pinfolen;

   while(pos<size) {
    int esinfolen=read_short(pmt,pos+3)&0xfff; 

    ca_pid=lookup_ca_pid(pmt,pos+5,pos+5+esinfolen,Conax.CA_ID);

    if (ca_pid>0) return ca_pid;

    pos+=5+esinfolen;
   }
  }

  return 0;
 }

 public static int print_pat(boolean full) throws Throwable {
  int pmt_pid=0;

  try {
   byte pat[]=MPEGSniffer.get_PAT();

   int pos=MPEGDefs.SECTION_DATA_OFF;

   int size=pat.length-4;

   Output.text("[Program Association Table]");
   String s=Utils.padded_string("svid",PAD)+Utils.padded_string("PID",PAD);
   Output.text(s);

   while(pos<size) {
    int prognum=read_short(pat,pos);
    int pid=read_short(pat,pos+2)&0x1fff;

    String s1="0x"+Utils.hex_value(prognum,4);
    String s2="0x"+Utils.hex_value(pid,4);

    s=Utils.padded_string(s1,PAD)+Utils.padded_string(s2,PAD);

    if (full) {
     Service srv=SrvUtils.current_service();
     DvbLocator dvblocator=(DvbLocator)srv.getLocator();

     String onid=Integer.toHexString(dvblocator.getOriginalNetworkId());
     String tsid=Integer.toHexString(dvblocator.getTransportStreamId());
     String svid=Integer.toHexString(prognum);

     srv=SrvUtils.srv_by_locator("dvb://"+onid+"."+tsid+"."+svid);

     if (srv!=null) {
      s+=srv.getName();
     }
    }

    Output.text(s);
    
    pos+=4;  
   }

  } catch(Throwable t) {}

  return pmt_pid;
 }

 private static String stream_type(int type) {
  String streamtype=null;

  type&=0xff;

  if (type<stream_types.length) {
   streamtype=stream_types[type];
  } else

  if ((type>=0x1C)&&(type<=0x7E)) streamtype="ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Reserved";
   else

  if (type==0x7f) streamtype="IPMP stream";
   else 

  if ((type>=0x80)&&(type<=0xff)) streamtype="User Private";

  return streamtype;
 }


 private static String desc_type(int type) {
  String desctype=null;

  type&=0xff;

  if (type<desc_types.length) {
   desctype=desc_types[type];
  } else

  if ((type>=36)&&(type<=63)) desctype="ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Reserved";
   else
  if ((type>=64)&&(type<=255)) desctype="User Private";

  return desctype;
 }

 private static String dopad(int num) {
  String str="";

  for(int i=0;i<num;i++) {
   str+=" ";
  }

  return str;
 }

 private static void print_descriptor(int pad,int tag,byte data[],int pos,int size) throws Throwable {
  String s=dopad(pad)+"DESC 0x"+Utils.hex_value(tag,2)+" ["+desc_type(tag)+"]";
  Output.text(s);

  switch(tag) {
   case MPEGDefs.CA_DESCRIPTOR:
    int ca_id=read_short(data,pos);
    int ca_pid=read_short(data,pos+2)&0x1fff;

    s=dopad(pad)+"- ca_id  0x"+Utils.hex_value(ca_id,4);
    Output.text(s);

    s=dopad(pad)+"- ca_pid 0x"+Utils.hex_value(ca_pid,4);
    Output.text(s);
    break;
   default:
    byte ddata[]=new byte[size];

    for(int i=0;i<size;i++) {
     ddata[i]=data[pos+i];
    }

    print_buf(pad+2,ddata);
    break;
  }
 }

 private static void print_desc_table(int pad,byte data[],int pos,int size) throws Throwable {
  while(pos<size) {
   int tag=(int)data[pos];
   int len=(int)data[pos+1];

   print_descriptor(pad,tag,data,pos+2,len);

   pos+=len+2;   
  }
 }

 public static void print_pmt(int svid) throws Throwable {
  int pmt_pid=get_PMT_pid(svid);

  if (pmt_pid>0) {
   byte pmt[]=MPEGSniffer.get_PMT(pmt_pid,svid);

   int pos=MPEGDefs.SECTION_DATA_OFF;

   int size=pmt.length-4;

   Output.text("[Program Map Table]");

   int pcr_pid=read_short(pmt,pos)&0x1fff;

   String s="- "+Utils.padded_string("pcr_pid",PAD2)+"0x"+Utils.hex_value(pcr_pid,4);
   Output.text(s);

   pos+=2;

   int pinfolen=read_short(pmt,pos)&0xfff; 

   print_desc_table(2,pmt,pos+2,pos+2+pinfolen);

   pos+=2+pinfolen;

   while(pos<size) {
    int esinfolen=read_short(pmt,pos+3)&0xfff;

    int stream_type=(int)pmt[pos]&0xff;
    int elementary_pid=read_short(pmt,pos+1)&0x1fff;

    s="- stream_type 0x"+Utils.hex_value(stream_type,2);
    Output.text(s);

    s="  ("+stream_type(stream_type)+")";
    Output.text(s);

    s="- "+Utils.padded_string("elementary_pid",PAD2)+"0x"+Utils.hex_value(elementary_pid,4);
    Output.text(s);

    print_desc_table(4,pmt,pos+5,pos+5+esinfolen);

    pos+=5+esinfolen;
   }
  }
 }

 public static int pid_by_svid(int svid) {
  try {
   byte pat[]=MPEGSniffer.get_PAT();

   int pos=MPEGDefs.SECTION_DATA_OFF;

   int size=pat.length-4;

   while(pos<size) {
    int prognum=read_short(pat,pos);
    int pid=read_short(pat,pos+2)&0x1fff;

    if (prognum==svid) return pid;
    
    pos+=4;  
   }

  } catch(Throwable t) {}

  return -1;
 }

 private static int lookup_ssu_svid(byte data[],int pos,int size) {
  int ssu_svid=-1;

  while(pos<size) {
   int tag=(int)data[pos];
   int len=(int)data[pos+1];

   if (tag==MPEGDefs.LINKAGE_DESCRIPTOR) {
    ssu_svid=read_short(data,pos+6);
    break;
   }

   pos+=len+2;   
  }

  return ssu_svid;
 }

 public static int get_ssu_svid() {
  int ssu_svid=-1;

/*
  try {
   byte nit[]=MPEGSniffer.get_NIT();

   int pos=MPEGDefs.SECTION_DATA_OFF;

   int size=nit.length-4;

   int netdesclen=read_short(nit,pos)&0xfff; 
   ssu_svid=lookup_ssu_svid(nit,pos+2,pos+2+netdesclen);
  } catch(Throwable t) {}
*/

  ssu_svid=0x3aca;

  return ssu_svid;
 }
  
 public static int get_ssu_pid(int ssu_svid) {
  int ssu_pid=-1;

  try {
   if (ssu_svid>0) {
    ssu_pid=pid_by_svid(ssu_svid);
   }
  } catch(Throwable t) {}

  return ssu_pid;
 }

 public static ESInfo[] get_pmt_streams(byte pmt[]) throws Throwable {
  ESInfo streams[]=null;
  Vector v=new Vector();

  if (pmt!=null) {
   int pos=MPEGDefs.SECTION_DATA_OFF;

   int size=pmt.length-4;

   pos+=2;

   int pinfolen=read_short(pmt,pos)&0xfff; 

   pos+=2+pinfolen;

   while(pos<size) {
    int esinfolen=read_short(pmt,pos+3)&0xfff;

    int stream_type=(int)pmt[pos]&0xff;
    int elementary_pid=read_short(pmt,pos+1)&0x1fff;

    v.addElement(new ESInfo(stream_type,elementary_pid));

    pos+=5+esinfolen;
   }
  }

  if (v.size()>0) {
   streams=new ESInfo[v.size()];

   for(int i=0;i<v.size();i++) {
    streams[i]=(ESInfo)v.elementAt(i);
   }
  }

  return streams;
 }

 public static ESInfo[] get_svid_streams(int svid) throws Throwable {
  ESInfo streams[]=null;
  Vector v=new Vector();

  int pmt_pid=get_PMT_pid(svid);

  if (pmt_pid>0) {
   byte pmt[]=MPEGSniffer.get_PMT(pmt_pid,svid);
   streams=get_pmt_streams(pmt);
  }

  return streams;
 }

}
