SASC
DVBSoftCAM

tps.c

Go to the documentation of this file.
00001 /*
00002  * Softcam plugin to VDR (C++)
00003  *
00004  * This code is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License
00006  * as published by the Free Software Foundation; either version 2
00007  * of the License, or (at your option) any later version.
00008  *
00009  * This code is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00017  * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
00018  */
00019 
00020 #include <stdlib.h>
00021 #include <string.h>
00022 
00023 #include <libsi/section.h>
00024 
00025 #include "viaccess.h"
00026 #include "log-viaccess.h"
00027 #include "tps.h"
00028 #include "opentv.h"
00029 #include "st20.h"
00030 
00031 #include "global.h"
00032 #include "scsetup.h"
00033 #include "system-common.h"
00034 #include "filter.h"
00035 #include "misc.h"
00036 #include "helper.h"
00037 
00038 #define CHECK_TIME    5*60*1000
00039 #define LOADBIN_TIME 60*60*1000
00040 #define TPSAU_TIME   30*60*1000
00041 
00042 //#define TRUST_BOXTIME // should we trust the local time on this machine?
00043 //#define DUMP_TPSAU "/var/tmp"
00044 
00045 #ifdef DUMP_TPSAU
00046 #include <unistd.h>
00047 #endif
00048 
00049 // -- cTransponderTime ---------------------------------------------------------
00050 
00051 class cTransponderTime : public cSimpleItem {
00052 private:
00053   time_t sattime;
00054   cTimeMs reftime;
00055   cMutex mutex;
00056   cCondVar wait;
00057   int source, transponder;
00058   bool hasHandler;
00059 public:
00060   cTransponderTime(int Source, int Transponder);
00061   bool Is(int Source, int Transponder) const;
00062   void Set(time_t st);
00063   time_t Now(void);
00064   void SetHasHandler(bool on) { hasHandler=on; }
00065   bool HasHandler(void) const { return hasHandler; }
00066   };
00067 
00068 cTransponderTime::cTransponderTime(int Source, int Transponder)
00069 {
00070   source=Source; transponder=Transponder;
00071 #ifdef TRUST_BOXTIME
00072   sattime=time(0);
00073 #else
00074   sattime=-1;
00075 #endif
00076   hasHandler=false;
00077 }
00078 
00079 bool cTransponderTime::Is(int Source, int Transponder) const
00080 {
00081   return source==Source && transponder==Transponder;
00082 }
00083 
00084 void cTransponderTime::Set(time_t st)
00085 {
00086   cMutexLock lock(&mutex);
00087   if(sattime<0 || reftime.Elapsed()>5*1000) {
00088     reftime.Set(-100);
00089     sattime=st;
00090     wait.Broadcast();
00091 
00092     char str[32];
00093     struct tm utm;
00094     st=Now();
00095     time_t now=time(0);
00096     gmtime_r(&st,&utm); asctime_r(&utm,str); stripspace(str);
00097     PRINTF(L_SYS_TIME,"%x:%x: %s (delta %ld)",source,transponder,str,st-now);
00098     }
00099 }
00100 
00101 time_t cTransponderTime::Now(void)
00102 {
00103   cMutexLock lock(&mutex);
00104   if(sattime<0) {
00105     if(!wait.TimedWait(mutex,3*1000)) {
00106       PRINTF(L_SYS_TIME,"%x:%x: unsuccessfull wait",source,transponder);
00107       return -1;
00108       }
00109     }
00110   return sattime+(reftime.Elapsed()/1000);
00111 }
00112 
00113 // -- cSatTimeHook -------------------------------------------------------------
00114 
00115 #ifndef TESTER
00116 
00117 class cSatTimeHook : public cLogHook {
00118 private:
00119   cTransponderTime *ttime;
00120 public:
00121   cSatTimeHook(cTransponderTime *Ttime);
00122   ~cSatTimeHook();
00123   virtual void Process(int pid, unsigned char *data);
00124   };
00125 
00126 cSatTimeHook::cSatTimeHook(cTransponderTime *Ttime)
00127 :cLogHook(HOOK_SATTIME,"sattime")
00128 {
00129   ttime=Ttime;
00130   pids.AddPid(0x14,0x70,0xfc);
00131   ttime->SetHasHandler(true);
00132 }
00133 
00134 cSatTimeHook::~cSatTimeHook()
00135 {
00136   ttime->SetHasHandler(false);
00137 }
00138 
00139 void cSatTimeHook::Process(int pid, unsigned char *data)
00140 {
00141   if(data && ttime) {
00142     if(data[0]==0x70) { // TDT
00143       SI::TDT tdt(data,false);
00144       tdt.CheckParse();
00145       ttime->Set(tdt.getTime());
00146       }
00147     else if(data[0]==0x73) { // TOT
00148       SI::TOT tot(data,false);
00149       if(!tot.CheckCRCAndParse()) return;
00150       ttime->Set(tot.getTime());
00151       }
00152     }
00153 }
00154 
00155 #endif //TESTER
00156 
00157 // -- cSatTime -----------------------------------------------------------------
00158 
00159 class cSatTime {
00160 private:
00161   static cMutex mutex;
00162   static cSimpleList<cTransponderTime> list;
00163   //
00164   cTransponderTime *ttime;
00165   //
00166   void CheckHandler(void);
00167 public:
00168   cSatTime(int Source, int Transponder);
00169   time_t Now(cSystem *sys);
00170   };
00171 
00172 cMutex cSatTime::mutex;
00173 cSimpleList<cTransponderTime> cSatTime::list;
00174 
00175 cSatTime::cSatTime(int Source, int Transponder)
00176 {
00177   cMutexLock lock(&mutex);
00178   for(ttime=list.First(); ttime; ttime=list.Next(ttime))
00179     if(ttime->Is(Source,Transponder)) break;
00180   if(!ttime) {
00181     ttime=new cTransponderTime(Source,Transponder);
00182     if(ttime) list.Add(ttime);
00183     PRINTF(L_SYS_TIME,"%x:%x: created new transpondertime",Source,Transponder);
00184     }
00185   else PRINTF(L_SYS_TIME,"%x:%x: using existing transpondertime",Source,Transponder);
00186 }
00187 
00188 time_t cSatTime::Now(cSystem *sys)
00189 {
00190   if(ttime) {
00191     if(!sys->TriggerHook(HOOK_SATTIME) && !ttime->HasHandler()) {
00192       cSatTimeHook *hook=new cSatTimeHook(ttime);
00193       sys->AddHook(hook);
00194       PRINTF(L_SYS_TIME,"added hook");
00195       }
00196     return ttime->Now();
00197     }
00198   return -1;
00199 }
00200 
00201 // -- cOpenTVModule ------------------------------------------------------------
00202 
00203 class cOpenTVModule {
00204 private:
00205   int id, modlen;
00206   unsigned char *mem;
00207   int received;
00208   info_header_t info;
00209   code_header_t code;
00210   data_header_t data;
00211   //
00212   bool Decompress(void);
00213   bool DoDecompress(unsigned char *out_ptr, const unsigned char *in_ptr, int dsize, int data_size, int usize);
00214   bool ParseSections(void);
00215   void Dump(void);
00216 public:
00217   cOpenTVModule(const unsigned char *data);
00218   cOpenTVModule(int Id, const unsigned char *data, int len);
00219   ~cOpenTVModule();
00220   int AddPart(const unsigned char *data, int len);
00221   const info_header_t *InfoHdr(void) const { return &info; }
00222   const code_header_t *CodeHdr(void) const { return &code; }
00223   const data_header_t *DataHdr(void) const { return &data; }
00224   //
00225   inline static int Id(const unsigned char *data) { return UINT16_BE(&data[3]); }
00226   inline static int Offset(const unsigned char *data) { return UINT32_BE(&data[16]); }
00227   inline static int Length(const unsigned char *data) { return UINT32_BE(&data[20]); }
00228   };
00229 
00230 cOpenTVModule::cOpenTVModule(const unsigned char *data)
00231 {
00232   id=Id(data); modlen=Length(data);
00233   received=0;
00234   mem=MALLOC(unsigned char,modlen);
00235 }
00236 
00237 cOpenTVModule::cOpenTVModule(int Id, const unsigned char *data, int len)
00238 {
00239   id=Id; modlen=received=len;
00240   mem=MALLOC(unsigned char,modlen);
00241   memcpy(mem,data,len);
00242   ParseSections();
00243 }
00244 
00245 cOpenTVModule::~cOpenTVModule()
00246 {
00247   free(mem);
00248 }
00249 
00250 int cOpenTVModule::AddPart(const unsigned char *data, int slen)
00251 {
00252   if(Id(data)==id) {
00253     int off=Offset(data);
00254     int mlen=Length(data);
00255     int rec=slen-28;
00256     if(mlen!=modlen || (off+rec)>mlen || !mem) {
00257       PRINTF(L_SYS_TPSAU,"length mismatch while adding to OpenTV module");
00258       return -1;
00259       }
00260     memcpy(&mem[off],data+24,rec);
00261     received+=rec;
00262     if(received==mlen) {
00263       if(!Decompress()) {
00264         PRINTF(L_SYS_TPSAU,"failed to decompress OpenTV module");
00265         return -1;
00266         }
00267       if(!ParseSections()) {
00268         PRINTF(L_SYS_TPSAU,"DATA & CODE section not located in OpenTV module");
00269         return -1;
00270         }
00271       Dump();
00272       return 1;
00273       }
00274     }
00275   return 0;
00276 }
00277 
00278 void cOpenTVModule::Dump(void)
00279 {
00280 #ifdef DUMP_TPSAU
00281 #warning Dumping TPS AU data
00282   {
00283   char fname[32];
00284   snprintf(fname,sizeof(fname),"%s/decomp.bin.%d.%x",DUMP_TPSAU,getpid(),(int)time(0));
00285   int fd=open(fname,O_CREAT|O_TRUNC|O_WRONLY,DEFFILEMODE);
00286   if(fd>=0) {
00287     write(fd,mem,modlen);
00288     close(fd);
00289     PRINTF(L_SYS_TPSAU,"dumped to file '%s'",fname);
00290     }
00291   }
00292 #endif
00293 }
00294 
00295 bool cOpenTVModule::ParseSections(void)
00296 {
00297   int sections=0;
00298   for(int idx=0; idx<modlen;) {
00299     unsigned int hdr=UINT32_BE(mem+idx);
00300     unsigned int s_len=UINT32_BE(mem+idx+4);
00301     switch(hdr) {
00302       case INFO_SECTION_HDR:
00303         info.magic=hdr;
00304         info.bsssize=UINT32_BE(mem+idx+8);
00305         info.stacksize=UINT32_BE(mem+idx+12);
00306         sections|=1;
00307         break;
00308       case CODE_SECTION_HDR:
00309         code.magic=hdr;
00310         code.size=s_len-8;
00311         code.m_id=UINT32_BE(mem+idx+8);
00312         code.entry_point=UINT16_BE(mem+idx+12);
00313         code.end=UINT16_BE(mem+idx+14);
00314         code.code=mem+idx+8;
00315         sections|=2;
00316         break;
00317       case DATA_SECTION_HDR:
00318         data.magic=hdr;
00319         data.dlen=s_len-8;
00320         data.data=mem+idx+8;
00321         sections|=4;
00322         break;
00323       case SWAP_SECTION_HDR:
00324       case GDBO_SECTION_HDR:
00325         break;
00326       case LAST_SECTION_HDR:
00327       case 0:
00328         idx=modlen;
00329         break;
00330       case COMP_SECTION_HDR:
00331         PRINTF(L_GEN_DEBUG,"OpenTV module still compressed in ParseSections()");
00332         return 0;
00333       default:
00334         PRINTF(L_SYS_TPSAU,"unknown section hdr %08x (%c%c%c%c) in OpenTV module",hdr,(hdr>>24)&0xFF,(hdr>>16)&0xFF,(hdr>>8)&0xFF,hdr&0xFF);
00335         break;
00336       }
00337     idx+=s_len;
00338     }
00339   return sections>=6;
00340 }
00341 
00342 bool cOpenTVModule::Decompress(void)
00343 {
00344   comp_header_t comp;
00345   comp.magic=UINT32_BE(mem);
00346   comp.csize=UINT32_BE(mem+4);
00347   comp.dsize=UINT32_BE(mem+8);
00348   comp.usize=UINT32_BE(mem+12);
00349   comp.end  =UINT8_BE(mem+16);
00350   if((COMP_SECTION_HDR!=comp.magic) || (comp.dsize<=comp.csize) || 
00351      (comp.usize>comp.dsize) || (comp.end>=1) || (comp.csize<=17))
00352     return true;
00353   unsigned char *decomp=MALLOC(unsigned char,comp.dsize);
00354   if(!decomp || !DoDecompress(decomp,mem+17,comp.dsize,comp.csize-17,comp.usize))
00355     return false;
00356   free(mem);
00357   mem=decomp; modlen=comp.dsize;
00358   return true;
00359 }
00360 
00361 #define BYTE() ((odd)?((in_ptr[0]&0x0F)|(in_ptr[1]&0xF0)):in_ptr[0])
00362 
00363 #define NIBBLE(__v) \
00364   do { \
00365     odd^=1; \
00366     if(odd) __v=in_ptr[0]&0xF0; \
00367     else {  __v=(in_ptr[0]&0xF)<<4; in_ptr++; } \
00368     } while(0)
00369 
00370 bool cOpenTVModule::DoDecompress(unsigned char *out_ptr, const unsigned char *in_ptr, int dsize, int data_size, int usize)
00371 {
00372   if(usize==0) return false;
00373   const unsigned char *data_start=in_ptr;
00374   unsigned char *out_start=out_ptr;
00375   unsigned char *out_end=out_ptr+usize;
00376   int odd=0;
00377   while(1) {
00378     unsigned char mask=BYTE(); in_ptr++;
00379     for(int cnt=8; cnt>0; mask<<=1,cnt--) {
00380       if(mask&0x80) {
00381         out_ptr[0]=BYTE(); in_ptr++;
00382         out_ptr++;
00383         }
00384       else {
00385         int off=0, len=0;
00386         unsigned char cmd=BYTE(); in_ptr++;
00387         switch(cmd>>4) {
00388           case 0x0:
00389           case 0x1:
00390           case 0x2:
00391           case 0x3:
00392           case 0x4:
00393           case 0x5:
00394           case 0x6:
00395             off=((cmd&0xF)<<8)+BYTE(); in_ptr++;
00396             len=((cmd>>4)&0x7)+3;
00397             break;
00398           case 0x7:
00399             {
00400             unsigned char high=BYTE(); in_ptr++;
00401             off=((high&0x7F)<<8)+BYTE(); in_ptr++;
00402             if((cmd==0x7F) && (high&0x80)) {
00403               len=BYTE(); in_ptr++;
00404               if(len==0xFF) {
00405                 len=BYTE(); in_ptr++;
00406                 len=((len<<8)+BYTE()+0x121)&0xFFFF; in_ptr++;
00407                 }
00408               else len+=0x22;
00409               }
00410             else {
00411               len=((cmd&0x0F)<<1)+3; if(high&0x80) len++;
00412               }
00413             break;
00414             }
00415           case 0x8:
00416           case 0x9:
00417           case 0xA:
00418           case 0xB:
00419             if(cmd&0x20) NIBBLE(off); else off=0;
00420             off=(off<<1)|(cmd&0x1F); len=2;
00421             break;
00422           case 0xC:
00423             off=cmd&0x0F; len=3;
00424             break;
00425           case 0xD:
00426           case 0xE:
00427           case 0xF:
00428             NIBBLE(off);
00429             off|=cmd&0x0F; len=((cmd>>4)&0x3)+2;
00430             break;
00431           }
00432         const unsigned char *from=out_ptr-(off+1);
00433         if(from<out_start || from>=out_ptr || len>(out_end-out_ptr)) {
00434           PRINTF(L_SYS_TPSAU,"length mismatch in OpenTV decompress");
00435           return false;
00436           }
00437         while(--len>=0) *out_ptr++=*from++;
00438         }
00439       if(out_end<=out_ptr) {
00440         if(out_end!=out_ptr) {
00441           PRINTF(L_SYS_TPSAU,"pointer mismatch in OpenTV decompress");
00442           return false;
00443           }
00444         int len=out_start+dsize-out_ptr;
00445         if(len>0) memmove(out_ptr,data_start+(data_size-(dsize-usize)),len);
00446         return true;
00447         }
00448       }
00449     }
00450 }
00451 
00452 #undef BYTE
00453 #undef NIBBLE
00454 
00455 // -- cTpsKey ------------------------------------------------------------------
00456 
00457 cTpsKey::cTpsKey(void)
00458 {
00459   timestamp=0;
00460 }
00461 
00462 void cTpsKey::Set(const cTpsKey *k)
00463 {
00464   timestamp=k->timestamp;
00465   opmode=k->opmode;
00466   memcpy(step,k->step,sizeof(step));
00467 }
00468 
00469 void cTpsKey::Set(const unsigned char *mem)
00470 {
00471   timestamp=*((unsigned int *)mem);
00472   if((unsigned int)timestamp>0x7FFFFFFFUL) timestamp=0x7FFFFFFFL;
00473   opmode=mem[52+3];
00474   for(int i=0; i<3; i++) {
00475     memcpy(step[i].key,&mem[4+i*16],16);
00476     step[i].mode=mem[52+i];
00477     }
00478 }
00479 
00480 void cTpsKey::Put(unsigned char *mem) const
00481 {
00482   *((unsigned int *)mem)=timestamp;
00483   mem[52+3]=opmode;
00484   for(int i=0; i<3; i++) {
00485     memcpy(&mem[4+i*16],step[i].key,16);
00486     mem[52+i]=step[i].mode;
00487     }
00488 }
00489 
00490 cString cTpsKey::ToString(bool hide)
00491 {
00492   unsigned char tmp[60];
00493   Put(&tmp[4]);
00494   *((unsigned int *)tmp)=crc32_le(0,&tmp[4],sizeof(tmp)-4);
00495   char str[420];
00496   HexStr(str,tmp,sizeof(tmp));
00497   return str;
00498 }
00499 
00500 // -- cTpsAuHook ---------------------------------------------------------------
00501 
00502 #ifndef TESTER
00503 
00504 #define BUFF_SIZE 20000
00505 
00506 class cTpsAuHook : public cLogHook {
00507 private:
00508   cOpenTVModule *mod;
00509   int pmtpid, aupid;
00510 public:
00511   cTpsAuHook(void);
00512   ~cTpsAuHook();
00513   virtual void Process(int pid, unsigned char *data);
00514   };
00515 
00516 #endif //TESTER
00517 
00518 // -- cTpsKeys -----------------------------------------------------------------
00519 
00520 cTpsKeys tpskeys;
00521 
00522 #define AU_STREAM 0
00523 #define AU_TPSBIN 1
00524 int tpsAuMode=AU_STREAM;
00525 
00526 cTpsKeys::cTpsKeys(void)
00527 :cStructListPlain<cTpsKey>("TPS keys","tps.cache",SL_READWRITE|SL_MISSINGOK|SL_WATCH|SL_NOPURGE)
00528 ,lastLoad(-LOADBIN_TIME)
00529 ,lastAu(-TPSAU_TIME)
00530 {
00531   first=last=0; algomem=0; loadlist=0;
00532 }
00533 
00534 cTpsKeys::~cTpsKeys()
00535 {
00536   free(algomem);
00537   delete loadlist;
00538 }
00539 
00540 const cTpsKey *cTpsKeys::GetKey(time_t t)
00541 {
00542   ListLock(false);
00543   cTpsKey *k;
00544   for(k=First(); k; k=Next(k)) if(t<k->Timestamp()) break;
00545   ListUnlock();
00546   return k;
00547 }
00548 
00549 const cTpsKey *cTpsKeys::GetV2Key(int id)
00550 {
00551   unsigned char tmp[56];
00552   memset(tmp,0,sizeof(tmp));
00553   cPlainKey *pk=keys.FindKey('V',id,MBC3('T','P','S'),16,0);
00554   if(pk) {
00555     pk->Get(&tmp[4+2*16]);
00556     tmp[52+2]=1;
00557     tmp[52+3]=0x1C;
00558     cTpsKey *tk=new cTpsKey;
00559     if(tk) {
00560       tk->Set(tmp);
00561       return tk;
00562       }
00563     }
00564   else PRINTF(L_SYS_KEY,"missing %.4x TPS key\n",id);
00565   return 0;
00566 }
00567 
00568 void cTpsKeys::Check(time_t now, cSystem *sys)
00569 {
00570   checkMutex.Lock();
00571   if(first==0 && last==0 && Count()>0)
00572     GetFirstLast();
00573   if(now>0 && lastCheck.Elapsed()>CHECK_TIME) {
00574     Purge(now);
00575     lastCheck.Set();
00576     }
00577   bool nokey=now+2*3600>last;
00578   if(tpsAuMode==AU_TPSBIN && lastLoad.Elapsed()>(nokey ? LOADBIN_TIME/60 : LOADBIN_TIME)) {
00579     PRINTF(L_SYS_TPSAU,"loading "TPSBIN" triggered");
00580     LoadBin();
00581     if(now>0) Purge(now);
00582     lastLoad.Set();
00583     }
00584   if(tpsAuMode==AU_STREAM && lastAu.Elapsed()>(nokey ? TPSAU_TIME/60 : TPSAU_TIME)) {
00585     if(ScSetup.AutoUpdate>0) {
00586       PRINTF(L_SYS_TPSAU,"TPS AU triggered");
00587       if(!sys->TriggerHook(HOOK_TPSAU)) {
00588         cTpsAuHook *hook=new cTpsAuHook;
00589         sys->AddHook(hook);
00590         PRINTF(L_SYS_TPSAU,"TPS AU hook added");
00591         }
00592       }
00593     lastAu.Set();
00594     }
00595   checkMutex.Unlock();
00596 }
00597 
00598 void cTpsKeys::Purge(time_t now)
00599 {
00600   PRINTF(L_SYS_TPSAU,"purging TPS keylist");
00601   bool del=false;
00602   ListLock(true);
00603   for(cTpsKey *k=First(); k;) {
00604     cTpsKey *n=Next(k);
00605     if(k->Timestamp()<now-3600) { Del(k); del=true; }
00606     k=n;
00607     }
00608   ListUnlock();
00609   if(del) {
00610     GetFirstLast();
00611     Modified();
00612     }
00613 }
00614 
00615 void cTpsKeys::Join(cSimpleList<cTpsKey> *nlist)
00616 {
00617   ListLock(true);
00618   cTpsKey *k;
00619   while((k=nlist->First())) {
00620     nlist->Del(k,false);
00621     cTpsKey *p=First();
00622     do {
00623       if(!p) {
00624         Add(k);
00625         Modified();
00626         break;
00627         }
00628       cTpsKey *n=Next(p);
00629       if(k->Timestamp()==p->Timestamp()) {
00630         p->Set(k);
00631         Modified();
00632         delete k;
00633         break;
00634         }
00635       if(k->Timestamp()>p->Timestamp() && (!n || k->Timestamp()<n->Timestamp())) {
00636         Add(k,p);
00637         Modified();
00638         break;
00639         }
00640       p=n;
00641       } while(p);
00642     }
00643   ListUnlock();
00644   delete nlist;
00645   GetFirstLast();
00646 }
00647 
00648 cString cTpsKeys::Time(time_t t)
00649 {
00650   char str[32];
00651   struct tm tm_r;
00652   strftime(str,sizeof(str),"%b %e %Y %T",localtime_r(&t,&tm_r));
00653   return str;
00654 }
00655 
00656 void cTpsKeys::GetFirstLast(void)
00657 {
00658   if(Count()>0) {
00659     ListLock(false);
00660     cTpsKey *k=First();
00661     first=last=k->Timestamp();
00662     for(; k; k=Next(k)) {
00663       if(k->Timestamp()<last)
00664         PRINTF(L_SYS_TPSAU,"TPS keys not in accending order!");
00665       last=k->Timestamp();
00666       }
00667     PRINTF(L_SYS_TPS,"%d TPS keys available (from %s to %s)",Count(),*Time(first),*Time(last));
00668     ListUnlock();
00669     }
00670   else {
00671     last=first=0;
00672     PRINTF(L_SYS_TPS,"no TPS keys available");
00673     }
00674 }
00675 
00676 bool cTpsKeys::ProcessAu(const cOpenTVModule *mod)
00677 {
00678   PRINTF(L_SYS_TPSAU,"processing TPS AU data");
00679 
00680   const code_header_t *codehdr=mod->CodeHdr();
00681   const data_header_t *datahdr=mod->DataHdr();
00682   const unsigned char *c=codehdr->code;
00683   const unsigned char *d=datahdr->data;
00684   unsigned int kd=0, cb1=0, cb2=0, cb3=0;
00685   for(unsigned int i=0; i<codehdr->size; i++) {
00686     if(c[i]==0x81) { // PushEA DS:$xxxx
00687       unsigned int addr=(c[i+1]<<8)|c[i+2];
00688       if(addr<(datahdr->dlen-3)) {
00689         if(d[addr]==0x79 && d[addr+1]==0x00 && d[addr+2]==0x79 && d[addr+3]==0x00)
00690           kd=addr;
00691 //XXX this needs proper fix sometime...
00692        else if(d[addr+1]==0x00 && d[addr+3]==0x00 && (d[addr+4]==3||d[addr+4]==2))
00693           kd=addr;
00694 //XXX
00695         else if(d[addr]==0x73 && d[addr+1]==0x25 && d[addr+2]==0xFA)
00696           cb1=addr;
00697         else if(d[addr]==0x64 && (d[addr+1]&0xB0)==0xB0 && d[addr+2]==0x24)
00698           cb2=addr;
00699         else if((d[addr]&0x60)==0x60 && (d[addr+1]&0xB0)==0xB0 && (d[addr+2]&0x20)==0x20)
00700           cb3=addr;
00701 /*
00702         else if(d[addr]==0x73 && d[addr+1]==0x25) {
00703           static const unsigned char scan1[] = { 0x28, 0x20, 0x20, 0xC0 };
00704           for(int j=2; j < 0xC; j++)
00705             if(!memcmp(&d[addr+j],scan1,sizeof(scan1))) { cb1=addr; break; }
00706           }
00707         else if(cb1 && !cb2) cb2=addr;
00708         else if(cb1 && cb2 && !cb3) cb3=addr;
00709 */
00710         }
00711       }
00712     }
00713   if(!kd || !cb1 || !cb2 || !cb3) {
00714     PRINTF(L_SYS_TPSAU,"couldn't locate all pointers in data section (%d,%d,%d,%d)",kd,cb1,cb2,cb3);
00715     return false;
00716     }
00717 
00718   unsigned int end=(kd>cb1 && kd>cb2 && kd>cb3) ? kd : datahdr->dlen;
00719   unsigned int off=min(cb1,min(cb2,cb3))-2;
00720   PRINTF(L_SYS_TPSAU,"pointers in data section kd=%d cb1=%d cb2=%d cb3=%d - dlen=%d off=%d end=%d - cb1=%d cb2=%d cb3=%d",kd,cb1,cb2,cb3,datahdr->dlen,off,end,cb1-off,cb2-off,cb3-off);
00721   RegisterAlgo3(d+off,cb1-off,cb2-off,cb3-off,end-off);
00722 
00723   const unsigned char *data=&d[kd];
00724   int seclen, numkeys;
00725   seclen=data[0] | (data[1]<<8);
00726   numkeys=data[2] | (data[3]<<8);
00727   int algo=data[4];
00728   int mkidx=data[5]&7;
00729   unsigned char *sec[7];
00730   sec[0]=(unsigned char *)data+6;
00731   for(int i=1; i<6; i++) sec[i]=sec[i-1]+seclen;
00732   sec[6]=sec[5]+numkeys;
00733   unsigned char key[16];
00734   cPlainKey *pk=keys.FindKey('V',0x7c00,MBC3('M','K',mkidx),16);
00735   if(!pk) {
00736     PRINTF(L_SYS_KEY,"missing V 7C00 TPSMK%d key",mkidx);
00737     return false;
00738     }
00739   pk->Get(key);
00740 
00741   if(sec[6]>=d+datahdr->dlen) {
00742     PRINTF(L_SYS_TPSAU,"section 5 exceeds buffer");
00743     return false;
00744     }
00745   int keylen=0;
00746   for(int i=0; i<numkeys; i++) keylen+=sec[5][i];
00747   keylen=(keylen+15)&~15;
00748   if(sec[6]+keylen>=d+datahdr->dlen) {
00749     PRINTF(L_SYS_TPSAU,"section 6 exceeds buffer");
00750     return false;
00751     }
00752   for(int i=0; i<keylen; i+=16) TpsDecrypt(&sec[6][i],algo,key);
00753 
00754   cSimpleList<cTpsKey> *nlist=new cSimpleList<cTpsKey>;
00755   for(int i=0; i<seclen; i++) {
00756     static const unsigned char startkey[] = { 0x01,0x01 };
00757     static const unsigned char startaes[] = { 0x09,0x10 };
00758     static const unsigned char startse[] = { 0x0a,0x10 };
00759     unsigned char tmp[56];
00760     tmp[0]=sec[0][i];
00761     tmp[1]=sec[1][i];
00762     tmp[2]=sec[2][i];
00763     tmp[3]=sec[3][i];
00764     if(CheckFF(tmp,4)) continue;
00765     int keyid=sec[4][i];
00766     int keylen=sec[5][keyid];
00767     if(keylen<32) continue;
00768     const unsigned char *tkey=sec[6];
00769     for(int j=0; j<keyid; j++) tkey+=sec[5][j];
00770 
00771     unsigned char ke[128];
00772     if(keylen!=45) {
00773       if(!Handle80008003(tkey,keylen,ke)) continue;
00774       tkey=ke;
00775       }
00776 
00777     if(memcmp(tkey,startkey,sizeof(startkey))) continue;
00778     tmp[52]=0;
00779     tmp[53]=tkey[5]; //tkey[4];
00780     tmp[54]=1;       //tkey[5];
00781     tmp[55]=0x1c;
00782     tkey+=9;
00783     if(memcmp(tkey,startaes,sizeof(startaes))) continue;
00784     memset(&tmp[4+ 0],0,16);
00785     memcpy(&tmp[4+16],&tkey[2],16);
00786     tkey+=18;
00787     if(memcmp(tkey,startse,sizeof(startse))) continue;
00788     memcpy(&tmp[4+32],&tkey[2],16);
00789     cTpsKey *k=new cTpsKey;
00790     if(k) { k->Set(tmp); nlist->Add(k); }
00791     }
00792   PRINTF(L_SYS_TPSAU,"got %d keys from AU data",nlist->Count());
00793   bool res=nlist->Count()>0;
00794   Join(nlist);
00795   return res;
00796 }
00797 
00798 bool cTpsKeys::LoadBin(void)
00799 {
00800   static const unsigned char mark[] = { 'T','P','S',0 };
00801   cFileMap *tpsbin=filemaps.GetFileMap(TPSBIN,FILEMAP_DOMAIN,false);
00802   if(!tpsbin) {
00803     PRINTF(L_SYS_TPS,"no filemap for "TPSBIN);
00804     return false;
00805     }
00806   else if(!tpsbin->Map()) {
00807     PRINTF(L_SYS_TPS,"mapping failed for "TPSBIN);
00808     return false;
00809     }
00810   const unsigned char *a=tpsbin->Addr();
00811   int len=tpsbin->Size();
00812   if(len<(68+56) || memcmp(mark,a+32,sizeof(mark)) || memcmp(mark,a+48,sizeof(mark))) {
00813     PRINTF(L_SYS_TPS,TPSBIN" format not recognised");
00814     tpsbin->Unmap();
00815     return false;
00816     }
00817 
00818   int size=*((const unsigned int *)(a+64));
00819   a+=68; len-=68;
00820   if(len<size) {
00821     PRINTF(L_SYS_TPS,TPSBIN" key data length mismatch");
00822     tpsbin->Unmap();
00823     return false;
00824     }
00825 
00826   cSimpleList<cTpsKey> *nlist=new cSimpleList<cTpsKey>;
00827   for(int i=0; i<size; i+=56,a+=56,len-=56) {
00828     if(*((const unsigned int *)a)==0x00000000L) break;
00829     unsigned char tmp[56];
00830     DecryptBin(a,tmp);
00831     cTpsKey *k=new cTpsKey;
00832     if(k) { k->Set(tmp); nlist->Add(k); }
00833     }
00834   PRINTF(L_SYS_TPSAU,"loaded %d keys from "TPSBIN" file",nlist->Count());
00835   Join(nlist);
00836 
00837   static const unsigned char alg_mark[] = { 'A','L','G','3' };
00838   while(len>64) {
00839     if(!memcmp(alg_mark,a,sizeof(alg_mark)) && *(a+11)==0x02) {
00840       size=*((const unsigned short *)(a+6));
00841       if(len<size) {
00842         PRINTF(L_SYS_TPS,TPSBIN" algo data length mismatch");
00843         break;
00844         }
00845       int off=a-tpsbin->Addr()+64;
00846       int cb1=*((const unsigned short *)(a+18))-off;
00847       int cb2=*((const unsigned short *)(a+30))-off;
00848       int cb3=*((const unsigned short *)(a+42))-off;
00849       PRINTF(L_SYS_TPSAU,"algo pointers in "TPSBIN" off=%04x cb1=%04x cb2=%04x cb3=%04x size=%04x",off,cb1,cb2,cb3,size);
00850       RegisterAlgo3(a+64,cb1,cb2,cb3,size);
00851       break;
00852       }
00853     a+=4; len-=4;
00854     }
00855 
00856   tpsbin->Unmap();
00857   return true;
00858 }
00859 
00860 void cTpsKeys::DecryptBin(const unsigned char *in, unsigned char *out)
00861 {
00862   unsigned int var2=*((unsigned int *)in);
00863   *((unsigned int *)out)=var2;
00864   for(int i=0; i<13; i++) {
00865     in+=4; out+=4;
00866     var2=(var2<<3) | (var2>>(32-3));
00867     unsigned int var1=*((unsigned int *)in) ^ var2;
00868     *((unsigned int *)out)=(var1<<(i+2)) | (var1>>(32-(i+2)));
00869     }
00870 }
00871 
00872 void cTpsKeys::PreLoad(void)
00873 {
00874   delete loadlist;
00875   loadlist=new cSimpleList<cTpsKey>;
00876   if(!loadlist) PRINTF(L_SYS_TPS,"no memory for loadlist");
00877 }
00878 
00879 void cTpsKeys::PostLoad(void)
00880 {
00881   if(loadlist) { Join(loadlist); loadlist=0; }
00882 }
00883 
00884 bool cTpsKeys::ParseLinePlain(const char *line)
00885 {
00886   unsigned char tmp[60];
00887   if(line[0]=='X') {
00888     if(line[1]=='S') {
00889       if(algomem) PRINTF(L_SYS_TPS,"multiple start extentions during cache load");
00890       if(sscanf(&line[2],"%x %x %x %x",&algolen,&cb1off,&cb2off,&cb3off)==4) {
00891         free(algomem);
00892         if((algomem=MALLOC(unsigned char,algolen))) {
00893           algoread=0;
00894           return true;
00895           }
00896         else PRINTF(L_SYS_TPS,"no memory for algo");
00897         }
00898       else PRINTF(L_SYS_TPS,"bad format in start extention");
00899       }
00900     else if(line[1]=='C') {
00901       if(algomem) {
00902         int off, len;
00903         unsigned int crc;
00904         if(sscanf(&line[2],"%x %x %n",&off,&crc,&len)==2) {
00905           line+=len+2;
00906           unsigned char buff[210];
00907           if((len=GetHex(line,buff,200,false))) {
00908             if(crc==crc32_le(0,buff,len) && off>=0 && off+len<=algolen) {
00909               memcpy(&algomem[off],buff,len);
00910               algoread+=len;
00911               if(algoread==algolen) {
00912                 RegisterAlgo3(algomem,cb1off,cb2off,cb3off,algolen);
00913                 free(algomem); algomem=0;
00914                 }
00915               return true;
00916               }
00917             }
00918           }
00919         PRINTF(L_SYS_TPS,"bad format in code extention");
00920         }
00921       else PRINTF(L_SYS_TPS,"unexpected code extention");
00922       }
00923     else PRINTF(L_SYS_TPS,"unknown extention during cache load");
00924     }
00925   else {
00926     if(GetHex(line,tmp,sizeof(tmp))) {
00927       unsigned int crc=crc32_le(0,&tmp[4],sizeof(tmp)-4);
00928       if(*((unsigned int *)tmp)==crc) {
00929         cTpsKey *k=new cTpsKey;
00930         if(k) { k->Set(&tmp[4]); if(loadlist) loadlist->Add(k); }
00931         return true;
00932         }
00933       else PRINTF(L_SYS_TPS,"CRC failed during cache load");
00934       }
00935     }
00936   return false;
00937 }
00938 
00939 void cTpsKeys::PostSave(FILE *f)
00940 {
00941   char str[420];
00942   unsigned char *mem;
00943   int len=0, cb1=0, cb2=0, cb3=0;
00944   if((mem=DumpAlgo3(len,cb1,cb2,cb3))) {
00945     fprintf(f,"XS %04X %04X %04X %04X\n",len,cb1,cb2,cb3);
00946     for(int i=0; i<len; i+=200) {
00947       int l=min(200,len-i);
00948       fprintf(f,"XC %04X %08X %s\n",i,crc32_le(0,&mem[i],l),HexStr(str,&mem[i],l));
00949       }
00950     free(mem);
00951     }
00952 }
00953 
00954 // -- cTpsAuHook ---------------------------------------------------------------
00955 
00956 #ifndef TESTER
00957 
00958 #define AUSID 0x12C0
00959 
00960 cTpsAuHook::cTpsAuHook(void)
00961 :cLogHook(HOOK_TPSAU,"tpsau")
00962 {
00963   mod=0; pmtpid=aupid=-1;
00964   pids.AddPid(0x0000,0x00,0xff); // PAT
00965 }
00966 
00967 cTpsAuHook::~cTpsAuHook()
00968 {
00969   delete mod;
00970 }
00971 
00972 void cTpsAuHook::Process(int pid, unsigned char *data)
00973 {
00974   if(data) {
00975     if(pid==0) { // PAT
00976       SI::PAT pat(data,false);
00977       if(pat.CheckCRCAndParse()) {
00978         SI::PAT::Association assoc;
00979         for(SI::Loop::Iterator it; pat.associationLoop.getNext(assoc,it);) {
00980           if(!assoc.isNITPid() && assoc.getServiceId()==AUSID) {
00981             pmtpid=assoc.getPid();
00982             PRINTF(L_SYS_TPSAU,"got PMT pid %04x for SID %04x",pmtpid,AUSID);
00983             cPid *pid=pids.First();
00984             if(pid && pid->filter) {
00985               pid->filter->Start(pmtpid,0x02,0xFF);
00986               pid->filter->Flush();
00987               }
00988             else PRINTF(L_GEN_DEBUG,"internal: no pid/filter in cTpsAuHook/pat");
00989             return;
00990             }
00991           }
00992         PRINTF(L_SYS_TPSAU,"no PMT pid found for SID %04x",AUSID);
00993         BailOut();
00994         }
00995       }
00996     else if(pid==pmtpid) { // PMT
00997       SI::PMT pmt(data,false);
00998       if(pmt.CheckCRCAndParse() && pmt.getServiceId()==AUSID) {
00999         SI::PMT::Stream stream;
01000         for(SI::Loop::Iterator it; pmt.streamLoop.getNext(stream,it); ) {
01001           if(stream.getStreamType()==0x05) {
01002             aupid=stream.getPid();
01003             PRINTF(L_SYS_TPSAU,"got AU pid %04x",aupid);
01004             cPid *pid=pids.First();
01005             if(pid && pid->filter) {
01006               pid->filter->Start(aupid,0x87,0xFF);
01007               pid->filter->Flush();
01008               }
01009             else PRINTF(L_GEN_DEBUG,"internal: no pid/filter in cTpsAuHook/pmt");
01010             return;
01011             }
01012           }
01013         PRINTF(L_SYS_TPSAU,"could not locate AU pid in PMT %04x data",pmtpid);
01014         BailOut();
01015         }
01016       }
01017     else if(pid==aupid) {
01018       if(cOpenTVModule::Id(data)==2) {
01019         if(!mod) mod=new cOpenTVModule(data);
01020         if(mod) {
01021           int r=mod->AddPart(data,SCT_LEN(data));
01022           if(r>0) {
01023             PRINTF(L_SYS_TPSAU,"received complete OpenTV module ID 2");
01024             if(tpskeys.ProcessAu(mod)) BailOut();
01025             r=-1;
01026             }            
01027           if(r<0) { delete mod; mod=0; }
01028           }
01029         }
01030       }
01031     }
01032 }
01033 
01034 #endif //TESTER
01035 
01036 // -- cTPSDecrypt --------------------------------------------------------------
01037 
01038 unsigned char *cTPSDecrypt::mem=0;
01039 int cTPSDecrypt::memLen=0;
01040 int cTPSDecrypt::cb1off=0;
01041 int cTPSDecrypt::cb2off=0;
01042 int cTPSDecrypt::cb3off=0;
01043 cMutex cTPSDecrypt::st20Mutex;
01044 cST20 cTPSDecrypt::st20;
01045 bool cTPSDecrypt::st20Inited=false;
01046 
01047 void cTPSDecrypt::TpsDecrypt(unsigned char *data, short mode, const unsigned char *key)
01048 {
01049   switch(mode) {
01050     case 0: break;
01051     case 1: cAES::SetKey(key); cAES::Decrypt(data,16); break;
01052     case 2: cRC6::SetKey(key,16); cRC6::Decrypt(data); break;
01053     case 3: if(mem) {
01054               if(!DecryptAlgo3(key,data))
01055                 PRINTF(L_SYS_TPS,"decrypt failed in algo 3");
01056               }
01057             else PRINTF(L_SYS_TPS,"no callbacks for algo 3 registered");
01058             break;
01059     default: PRINTF(L_SYS_TPS,"unknown TPS decryption algo %d",mode); break;
01060     }
01061 }
01062 
01063 bool cTPSDecrypt::RegisterAlgo3(const unsigned char *data, int cb1, int cb2, int cb3, int len)
01064 {
01065   cMutexLock lock(&st20Mutex);
01066   free(mem);
01067   if(!(mem=MALLOC(unsigned char,len))) return false;
01068   memcpy(mem,data,len);
01069   memLen=len; cb1off=cb1; cb2off=cb2; cb3off=cb3;
01070   st20Inited=false;
01071   PRINTF(L_SYS_TPSAU,"registered callbacks for algo 3");
01072   return true;
01073 }
01074 
01075 unsigned char *cTPSDecrypt::DumpAlgo3(int &len, int &cb1, int &cb2, int &cb3)
01076 {
01077   cMutexLock lock(&st20Mutex);
01078   if(!mem) return 0;
01079   unsigned char *buff=MALLOC(unsigned char,memLen);
01080   if(!buff) return 0;
01081   memcpy(buff,mem,memLen);
01082   len=memLen; cb1=cb1off; cb2=cb2off; cb3=cb3off;
01083   return buff;
01084 }
01085 
01086 bool cTPSDecrypt::InitST20(void)
01087 {
01088   if(!mem) return false;
01089   if(!st20Inited) {
01090     st20.SetFlash(mem,memLen);
01091     st20.SetRam(NULL,0x10000);
01092     st20Inited=true;
01093     }
01094   return true;
01095 }
01096 
01097 bool cTPSDecrypt::Handle80008003(const unsigned char *src, int len, unsigned char *dest)
01098 {
01099   cMutexLock lock(&st20Mutex);
01100   if(cb1off && InitST20()) {
01101     for(int i=0; i<len; i++) st20.WriteByte(RAMS+0x400+i,src[i]);
01102     st20.WriteShort(RAMS+0x0,0x8000);
01103     st20.WriteShort(RAMS+0x2,0x8003);
01104     st20.WriteWord(RAMS+0x8,RAMS+0x400);
01105     st20.Init(FLASHS+cb1off,RAMS+0xF000);
01106     st20.SetCallFrame(0,RAMS,0,0);
01107     int err=st20.Decode(1000);
01108     if(err<0) {
01109       PRINTF(L_SYS_TPSAU,"ST20 processing failed in callback1 (%d)",err);
01110       return false;
01111       }
01112     for(int i=0; i<0x2D; i++) dest[i]=st20.ReadByte(RAMS+0x400+i);
01113     return true;
01114     }
01115   return false;
01116 }
01117 
01118 bool cTPSDecrypt::DecryptAlgo3(const unsigned char *key, unsigned char *data)
01119 {
01120   cMutexLock lock(&st20Mutex);
01121   if(cb2off && cb3off && InitST20()) {
01122     for(int i=0; i<16; i++) st20.WriteByte(RAMS+0x400+i,key[i]);
01123     st20.Init(FLASHS+cb2off,RAMS+0xF000);
01124     st20.SetCallFrame(0,RAMS+0x400,RAMS+0x800,0);
01125     int err=st20.Decode(30000);
01126     if(err<0) {
01127       PRINTF(L_SYS_TPS,"ST20 processing failed in callback2 (%d)",err);
01128       return false;
01129       }
01130 
01131     for(int i=0; i<16; i++) st20.WriteByte(RAMS+0x400+i,data[i]);
01132     st20.Init(FLASHS+cb3off,RAMS+0xF000);
01133     st20.SetCallFrame(0,RAMS+0x400,RAMS+0x1000,RAMS+0x800);
01134     err=st20.Decode(40000);
01135     if(err<0) {
01136       PRINTF(L_SYS_TPS,"ST20 processing failed in callback3 (%d)",err);
01137       return false;
01138       }
01139     for(int i=0; i<16; i++) data[i]=st20.ReadByte(RAMS+0x1000+i);
01140     return true;
01141     }
01142   return false;
01143 }
01144 
01145 // -- cTPS ---------------------------------------------------------------------
01146 
01147 cTPS::cTPS(void)
01148 {
01149   sattime=0;
01150 }
01151 
01152 cTPS::~cTPS()
01153 {
01154   delete sattime;
01155 }
01156 
01157 int cTPS::Decrypt(cSystem *sys, int Source, int Transponder, unsigned char *data, int len)
01158 {
01159   if(!sattime) {
01160     sattime=new cSatTime(Source,Transponder);
01161     if(!sattime) {
01162       PRINTF(L_SYS_TPS,"failed to create time class");
01163       return -1;
01164       }
01165     }
01166   time_t now=sattime->Now(sys);
01167   tpskeys.Check(now,sys);
01168   if(now<0) return -1;
01169 
01170   const cTpsKey *k=tpskeys.GetKey(now);
01171   doPost=0;
01172   int opmode=k?k->Opmode():4, doTPS=0, doPre=0, hasDF=0, ret=0;
01173   if((opmode&4) && data[0]==0xD2 && data[1]==0x01 && data[2]==0x01) {
01174     data+=3; len-=3; ret=3;
01175     if(data[0]==0x90) PRINTF(L_SYS_TPS,"TPS v1 is no longer supported");
01176     if(data[0]==0x40) data[0]=0x90;
01177     doTPS=1;
01178     if(!k) k=tpskeys.GetV2Key(0x7c00);
01179     }
01180   for(int i=0; i<len; i+=data[i+1]+2) {
01181     if(!k && (doTPS || doPre || doPost)) {
01182       PRINTF(L_SYS_TPS,"no TPS key available for current time of day");
01183       return -1;
01184       }
01185     switch(data[i]) {
01186       case 0xDF:
01187         if(!(opmode&4)) doTPS =(0x6996>>((data[i+2]&0xF)^(data[i+2]>>4)))&1;
01188         if(opmode&8)    doPre =(0x6996>>((data[i+3]&0xF)^(data[i+3]>>4)))&1;
01189         if(opmode&16)   doPost=(0x6996>>((data[i+4]&0xF)^(data[i+4]>>4)))&1;
01190         hasDF=1;
01191         break;
01192       case 0xEA:
01193         if(doPre) TpsDecrypt(&data[i+2],k->Mode(0),k->Key(0));
01194         if(doTPS) TpsDecrypt(&data[i+2],(hasDF)?k->Mode(1):1,k->Key(1));
01195         if(doPost) { postMode=k->Mode(2); memcpy(postKey,k->Key(2),sizeof(postKey)); }
01196         break;
01197       }
01198     }
01199   return ret;
01200 }
01201 
01202 void cTPS::PostProc(unsigned char *cw)
01203 {
01204   if(doPost) TpsDecrypt(cw,postMode,postKey);
01205 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines