|
SASC
DVBSoftCAM
|
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 }