SASC-NG
DVBSoftCAM

system.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 #include <unistd.h>
00023 
00024 #include <vdr/tools.h>
00025 
00026 #include <openssl/md5.h>
00027 
00028 #include "system.h"
00029 #include "cam.h"
00030 #include "global.h"
00031 #include "data.h"
00032 #include "override.h"
00033 #include "opts.h"
00034 #include "log-core.h"
00035 #include "i18n.h"
00036 
00037 // --- cFeature ----------------------------------------------------------------
00038 
00039 cFeature Feature;
00040 
00041 bool cFeature::keyfile=false;
00042 bool cFeature::smartcard=false;
00043 
00044 void cFeature::NeedsKeyFile(void)
00045 {
00046   if(!keyfile) PRINTF(L_CORE_DYN,"feature: using feature KEYFILE");
00047   keyfile=true;
00048 }
00049 
00050 void cFeature::NeedsSmartCard(void)
00051 {
00052   if(!smartcard) PRINTF(L_CORE_DYN,"feature: using feature SMARTCARD");
00053   smartcard=true;
00054 }
00055 
00056 // -- cKeySnoop ----------------------------------------------------------------
00057 
00058 cKeySnoop::cKeySnoop(cSystem *Sys, int Type, int Id, int Keynr)
00059 {
00060   ok=false;
00061   sys=Sys; type=Type; id=Id; keynr=Keynr;
00062 }
00063 
00064 cKeySnoop::~cKeySnoop()
00065 {
00066   if(!ok) sys->KeyFail(type,id,keynr);
00067 }
00068 
00069 void cKeySnoop::OK(cPlainKey *pk)
00070 {
00071   sys->KeyOK(pk);
00072   ok=true;
00073 }
00074 
00075 // -- cLogHook -----------------------------------------------------------------
00076 
00077 cLogHook::cLogHook(int Id, const char *Name)
00078 {
00079   id=Id; name=Name;
00080   bailOut=false;
00081 }
00082 
00083 // -- cSystem ------------------------------------------------------------------
00084 
00085 #define MAX_CHID 10
00086 
00087 struct EcmCheck {
00088   union {
00089     struct {
00090       bool startFlag;
00091       int current, chids[MAX_CHID];
00092       } sys06;
00093     } caid;
00094   };
00095 
00096 int cSystem::foundKeys=0;
00097 int cSystem::newKeys=0;
00098 
00099 cSystem::cSystem(const char *Name, int Pri)
00100 {
00101   name=Name; pri=Pri;
00102   currentKeyStr[0]=0; doLog=true; cam=0; logecm=0;
00103   check=new struct EcmCheck;
00104   memset(check,0,sizeof(struct EcmCheck));
00105   memset(cw,0,sizeof(cw));
00106   // default config
00107   maxEcmTry=2; // try to get a key X times from the same ECM pid (default)
00108   hasLogger=false;
00109   needsLogger=false;
00110   local=true;
00111   needsDescrData=false;
00112   constant=false;
00113 }
00114 
00115 cSystem::~cSystem()
00116 {
00117   delete check;
00118   if(logecm && cam) cam->LogEcmStatus(logecm,false);
00119   delete logecm;
00120 }
00121 
00122 void cSystem::StartLog(const cEcmInfo *ecm, int caid)
00123 {
00124   if(!logecm || logecm->caId!=caid) {
00125     if(logecm && cam) cam->LogEcmStatus(logecm,false);
00126     delete logecm;
00127     logecm=new cEcmInfo(ecm);
00128     logecm->caId=caid; logecm->emmCaId=0;
00129     if(cam) cam->LogEcmStatus(logecm,true);
00130     }
00131 }
00132 
00133 void cSystem::AddHook(cLogHook *hook)
00134 {
00135   if(cam) cam->AddHook(hook);
00136 }
00137 
00138 bool cSystem::TriggerHook(int id)
00139 {
00140   return cam && cam->TriggerHook(id);
00141 }
00142 
00143 void cSystem::CaidsChanged(void)
00144 {
00145   cGlobal::CaidsChanged();
00146 }
00147 
00148 void cSystem::ParseCADescriptor(cSimpleList<cEcmInfo> *ecms, unsigned short sysId, int source, const unsigned char *data, int len)
00149 {
00150   const int pid=WORD(data,2,0x1FFF);
00151   switch(sysId>>8) {
00152     case 0x01: // Seca style
00153       for(int p=2; p<len; p+=15) {
00154         cEcmInfo *n=new cEcmInfo(name,WORD(data,p,0x1FFF),sysId,WORD(data,p+2,0xFFFF));
00155         if(data[p+4]==0xFF) n->SetDataIdx(p+5);
00156         ecms->Add(n);
00157         }
00158       break;
00159     case 0x05: // Viaccess style
00160       for(int p=4; p<len; p+=2+data[p+1])
00161         if(data[p]==0x14)
00162           ecms->Add(new cEcmInfo(name,pid,sysId,(data[p+2]<<16)|(data[p+3]<<8)|(data[p+4]&0xF0)));
00163       break;
00164     default:   // default style
00165       ecms->Add(new cEcmInfo(name,pid,sysId,0));
00166       break;
00167     }
00168 }
00169 
00170 void cSystem::ParseCAT(cPids *pids, const unsigned char *buffer, int source, int transponder)
00171 {
00172   if(buffer[0]==0x09) {
00173     int caid=WORD(buffer,2,0xFFFF);
00174     int pid=WORD(buffer,4,0x1FFF);
00175     if(overrides.AddEmmPids(caid,source,transponder,pids,pid)) return;
00176     switch(caid>>8) {
00177       case 0x01: // Seca style (82/84)
00178         if(buffer[1]>4) {
00179           pids->AddPid(pid,0x82,0xFF); // Unique updates
00180           for(int i=7, nn=buffer[6] ; nn ; nn--,i+=4)
00181             pids->AddPid(WORD(buffer,i,0x1FFF),0x84,0xFF); // Shared updates
00182           }
00183         break;
00184       case 0x05: // Viaccess style (88/8c/8d/8e)
00185         pids->AddPid(pid,0x88,0xF9); // mismatching 8a
00186         pids->AddPid(pid,0x8D,0xFF);
00187         break;
00188       case 0x0d: // Cryptoworks style (82/84/86/88/89)
00189         pids->AddPid(pid,0x82,0xF9); // mismatching 80
00190         pids->AddPid(pid,0x88,0xFE);
00191         break;
00192       case 0x18: // Nagra style (82/83)
00193         pids->AddPid(pid,0x82,0xFE);
00194         break;
00195       default:   // default style (82)
00196         pids->AddPid(pid,0x82,0xFF);
00197         break;
00198       }
00199     }
00200 }
00201 
00202 int cSystem::CheckECM(const cEcmInfo *ecm, const unsigned char *data, bool sync)
00203 {
00204   switch(ecm->caId>>8) {
00205     case 0x06: // Irdeto
00206       {
00207       const int chid=WORD(data,6,0xFFFF);
00208       // if multiple channel id's, use current one only
00209       if(sync && check->caid.sys06.current>0 && chid!=check->caid.sys06.current) {
00210         PRINTF(L_CORE_ECMPROC,"ecmcheck(%s): chid %04x != current %04x",name,chid,check->caid.sys06.current);
00211         return 3;
00212         }
00213 /*
00214 //XXX is that still needed? Seems to conflict with some Irdeto2 provider
00215       // search for fake channel id's on Stream
00216       const int cur=data[4];
00217       const int max=data[5];
00218       if(!sync && max>0 && max<MAX_CHID) {
00219         if(cur==0) check->caid.sys06.startFlag=true;
00220         if(check->caid.sys06.startFlag) {
00221           if(cur<=max) check->caid.sys06.chids[cur]=chid;
00222           if(cur==max) {
00223             for(int i=0 ; i<max ; i++) {
00224               if(check->caid.sys06.chids[i]==0) {
00225                 check->caid.sys06.startFlag=false;
00226                 PRINTF(L_CORE_ECMPROC,"ecmcheck(%s): zero chid",name);
00227                 return 1;
00228                 }
00229               for(int j=i+1 ; j<=max ; j++) {
00230                 if(check->caid.sys06.chids[i]==check->caid.sys06.chids[j]) {
00231                   check->caid.sys06.startFlag=false;
00232                   PRINTF(L_CORE_ECMPROC,"ecmcheck(%s): duplicate chid %04x",name,check->caid.sys06.chids[i]);
00233                   return 1;
00234                   }
00235                 }
00236               }
00237             }
00238           }
00239         }
00240 */
00241       break;
00242       }
00243     }
00244   return 0;
00245 }
00246 
00247 void cSystem::CheckECMResult(const cEcmInfo *ecm, const unsigned char *data, bool result)
00248 {
00249   switch(ecm->caId>>8) {
00250     case 0x06: // Irdeto
00251       check->caid.sys06.current=result ? WORD(data,6,0xFFFF) : 0;
00252       break;
00253     }
00254 }
00255 
00256 void cSystem::KeyOK(cPlainKey *pk)
00257 {
00258   if(lastkey.NotLast(pk->type,pk->id,pk->keynr)) {
00259     strn0cpy(currentKeyStr,pk->ToString(false),sizeof(currentKeyStr));
00260     PRINTF(L_CORE_ECM,"system: using key %s",*pk->ToString(true));
00261     doLog=true;
00262     }
00263 }
00264 
00265 void cSystem::KeyOK(const char *txt)
00266 {
00267   snprintf(currentKeyStr,sizeof(currentKeyStr),"%s (%s)\n",txt?txt:name,tr("undisclosed key"));
00268   doLog=true;
00269 }
00270 
00271 void cSystem::KeyFail(int type, int id, int keynr)
00272 {
00273   keys.Trigger(type,id,keynr);
00274   if(lastkey.NotLast(type,id,keynr) && doLog)
00275     PRINTF(L_CORE_ECM,"system: no key found for %s",*keys.KeyString(type,id,keynr));
00276 }
00277 
00278 // -- cSystemLink --------------------------------------------------------------
00279 
00280 cSystemLink::cSystemLink(const char *Name, int Pri)
00281 {
00282   name=Name; pri=Pri;
00283   opts=0; noFF=false;
00284   cSystems::Register(this);
00285 }
00286 
00287 cSystemLink::~cSystemLink()
00288 {
00289   delete opts;
00290 }
00291 
00292 // -- cSystems -----------------------------------------------------------------
00293 
00294 cSystemLink *cSystems::first=0;
00295 int cSystems::nextSysIdent=0x1000;
00296 
00297 void cSystems::Register(cSystemLink *sysLink)
00298 {
00299   PRINTF(L_CORE_DYN,"systems: registering CA system %s, pri %d, ident %04X",sysLink->name,sysLink->pri,nextSysIdent);
00300   sysLink->next=first;
00301   sysLink->sysIdent=nextSysIdent++;
00302   first=sysLink;
00303 }
00304 
00305 int cSystems::CanHandle(int SysId, bool ff)
00306 {
00307   for(cSystemLink *sl=first; sl; sl=sl->next)
00308     if(sl->CanHandle(SysId))
00309       return (sl->noFF && ff) ? -1:1;
00310   return 0;
00311 }
00312 
00313 cSystemLink *cSystems::FindByName(const char *Name)
00314 {
00315   cSystemLink *sl=first;
00316   while(sl) {
00317     if(!strcasecmp(sl->name,Name)) return sl;
00318     sl=sl->next;
00319     }
00320   return 0;
00321 }
00322 
00323 cSystemLink *cSystems::FindById(unsigned short SysId, bool ff, int oldPri)
00324 {
00325   // all pri's are negative!
00326   // oldPri = 0 -> get highest pri system
00327   // oldPri < 0 -> get highest pri system with pri<oldPri
00328   // oldPri > 0 -> get lowest pri system
00329 
00330   cSystemLink *sl=first, *csl=0;
00331   while(sl) {
00332     if((!ff || !sl->noFF) && sl->CanHandle(SysId)
00333        && sl->pri<oldPri
00334        && (!csl 
00335            || (oldPri<=0 && sl->pri>csl->pri)
00336            || (oldPri>0 && sl->pri<csl->pri)
00337           )
00338        ) csl=sl;
00339     sl=sl->next;
00340     }
00341   return csl;
00342 }
00343 
00344 cSystemLink *cSystems::FindByIdent(int ident)
00345 {
00346   cSystemLink *sl=first;
00347   while(sl) {
00348     if(sl->sysIdent==ident) return sl;
00349     sl=sl->next;
00350     }
00351   return 0;
00352 }
00353 
00354 cSystem *cSystems::FindBySysId(unsigned short SysId, bool ff, int oldPri)
00355 {
00356   cSystemLink *csl=FindById(SysId,ff,oldPri);
00357   return csl ? csl->Create() : 0;
00358 }
00359 
00360 int cSystems::FindIdentBySysId(unsigned short SysId, bool ff, int &Pri)
00361 {
00362   cSystemLink *csl=FindById(SysId,ff,Pri);
00363   if(csl) {
00364     Pri=csl->pri;
00365     return csl->sysIdent;
00366     }
00367   return 0;
00368 }
00369 
00370 int cSystems::FindIdentBySysName(unsigned short SysId, bool ff, const char *Name, int &Pri)
00371 {
00372   cSystemLink *csl=FindByName(Name);
00373   if(csl && (!ff || !csl->noFF) && csl->CanHandle(SysId)) {
00374     Pri=csl->pri;
00375     return csl->sysIdent;
00376     }
00377   return 0;
00378 }
00379 
00380 cSystem *cSystems::FindBySysIdent(int ident)
00381 {
00382   cSystemLink *csl=FindByIdent(ident);
00383   return csl ? csl->Create() : 0;
00384 }
00385 
00386 bool cSystems::Init(const char *cfgdir)
00387 {
00388   int num=0;
00389   PRINTF(L_CORE_LOAD,"** registered systems:");
00390   for(cSystemLink *sl=first; sl; sl=sl->next, num++)
00391     PRINTF(L_CORE_LOAD,"** %-16s  (pri %3d)",sl->name,sl->pri);
00392   if(num<1)
00393     PRINTF(L_GEN_WARN,"No registered systems! Won't decode anything! Where are your libs?");
00394   for(cSystemLink *sl=first; sl; sl=sl->next)
00395     if(!sl->Init(cfgdir)) return false;
00396   return true;
00397 }
00398 
00399 void cSystems::Clean(void)
00400 {
00401   for(cSystemLink *sl=first; sl; sl=sl->next) sl->Clean();
00402 }
00403 
00404 bool cSystems::ConfigParse(const char *Name, const char *Value)
00405 {
00406   char sysName[32];
00407   unsigned int i;
00408   for(i=0; Name[i] && Name[i]!='.' && i<sizeof(sysName); i++)
00409     sysName[i]=Name[i];
00410   if(Name[i]) {
00411     sysName[i]=0; i++;
00412     cSystemLink *sl=FindByName(sysName);
00413     if(sl && sl->opts) return sl->opts->Parse(&Name[i],Value);
00414     }
00415   return false;
00416 }
00417 
00418 void cSystems::ConfigStore(bool AsIs)
00419 {
00420   for(cSystemLink *sl=first; sl; sl=sl->next)
00421     if(sl->opts && sl->opts->Store(AsIs)) sl->NewConfig();
00422 }
00423 
00424 cOpts *cSystems::GetSystemOpts(bool start)
00425 {
00426   static cSystemLink *sl=0;
00427   sl=(start || !sl) ? first : sl->next;
00428   while(sl) {
00429     if(sl->opts) return sl->opts;
00430     sl=sl->next;
00431     }
00432   return 0;
00433 }
00434 
00435 // -- cMsgCache ----------------------------------------------------------------
00436 
00437 #define FREE   0x00 // modes
00438 #define FAIL1  0x01
00439 #define FAIL2  0x3D
00440 #define FAILN  0x3E
00441 #define GOOD   0x3F
00442 #define MASK   0x3F
00443 #define QUEUED 0x40
00444 #define WAIT   0x80
00445 
00446 struct Cache {
00447   int crc;
00448   int mode;
00449   };
00450 
00451 cMsgCache::cMsgCache(int NumCache, int StoreSize)
00452 {
00453   numCache=NumCache;
00454   storeSize=StoreSize;
00455   ptr=0; stores=0; maxFail=2;
00456   caches=MALLOC(struct Cache,numCache);
00457   if(caches) {
00458     Clear();
00459     if(storeSize>0) {
00460       stores=MALLOC(unsigned char,numCache*storeSize);
00461       if(!stores) PRINTF(L_GEN_ERROR,"msgcache: no memory for store area");
00462       }
00463     }
00464   else PRINTF(L_GEN_ERROR,"msgcache: no memory for cache");
00465 }
00466 
00467 cMsgCache::~cMsgCache()
00468 {
00469   free(caches);
00470   free(stores);
00471 }
00472 
00473 void cMsgCache::SetMaxFail(int maxfail)
00474 {
00475   maxFail=min(maxfail,FAILN);
00476   PRINTF(L_CORE_MSGCACHE,"%d/%p: maxFail set to %d",getpid(),this,maxFail);
00477 }
00478 
00479 void cMsgCache::Clear(void)
00480 {
00481   cMutexLock lock(&mutex);
00482   memset(caches,0,sizeof(struct Cache)*numCache);
00483   ptr=0;
00484   PRINTF(L_CORE_MSGCACHE,"%d/%p: clear",getpid(),this);
00485 }
00486 
00487 struct Cache *cMsgCache::FindMsg(int crc)
00488 {
00489   int i=ptr;
00490   while(1) {
00491     if(--i<0) i=numCache-1;
00492     struct Cache * const s=&caches[i];
00493     if(!s->mode) break;
00494     if(s->crc==crc) return s;
00495     if(i==ptr) break;
00496     }
00497   return 0;
00498 }
00499 
00500 // returns:
00501 // -1 - msg cached as failed (max failed)
00502 // 0  - msg cached as good, result stored
00503 // >0 - msg not cached, queue id
00504 int cMsgCache::Get(const unsigned char *msg, int len, unsigned char *store)
00505 {
00506   unsigned char md[16];
00507   int crc=crc32_le(0,MD5(msg,len,md),16);
00508   cMutexLock lock(&mutex);
00509   if(!caches || (storeSize>0 && !stores)) return -1; // sanity
00510   struct Cache *s;
00511   while((s=FindMsg(crc))) {
00512     if(!(s->mode&QUEUED)) break;
00513     s->mode|=WAIT;
00514     PRINTF(L_CORE_MSGCACHE,"%d/%p: msg already queued. waiting to complete",getpid(),this);
00515     wait.Wait(mutex);
00516     }
00517   int id;
00518   if(!s) {
00519     while(1) {
00520       s=&caches[ptr];
00521       if(!(s->mode&QUEUED)) break;
00522       s->mode|=WAIT;
00523       PRINTF(L_CORE_MSGCACHE,"%d/%p: queue overwrite protection id=%d",getpid(),this,ptr+1);
00524       wait.Wait(mutex); // don't overwrite queued msg's
00525       }
00526     id=ptr+1;
00527     s->crc=crc;
00528     s->mode=QUEUED;
00529     PRINTF(L_CORE_MSGCACHE,"%d/%p: queued msg with id=%d",getpid(),this,id);
00530     ptr++; if(ptr>=numCache) { ptr=0; PRINTF(L_CORE_MSGCACHE,"msgcache: roll-over (%d)",numCache); }
00531     return id;
00532     }
00533   else {
00534     id=(s-&caches[0])+1;
00535     if(s->mode==GOOD) {
00536       if(store && storeSize>0)
00537         memcpy(store,&stores[(id-1)*storeSize],storeSize);
00538       PRINTF(L_CORE_MSGCACHE,"%d/%p: msg is cached as GOOD (%d)",getpid(),this,id);
00539       return 0;
00540       }
00541     else if(s->mode>=FAIL1 && s->mode<=FAIL2) {
00542       PRINTF(L_CORE_MSGCACHE,"%d/%p: msg is cached as FAIL%d (%d)",getpid(),this,s->mode,id);
00543       s->mode|=QUEUED;
00544       return id;
00545       }
00546     else {
00547       PRINTF(L_CORE_MSGCACHE,"%d/%p: msg is cached as FAILN (%d)",getpid(),this,id);
00548       return -1;
00549       }
00550     }
00551 }
00552 
00553 int cMsgCache::Cache(int id, bool result, const unsigned char *store)
00554 {
00555   cMutexLock lock(&mutex);
00556   if(id<1 || !caches || (storeSize>0 && !stores)) return 0; // sanity
00557   struct Cache *s=&caches[id-1];
00558   LBSTARTF(L_CORE_MSGCACHE);
00559   LBPUT("%d/%p: de-queued msg with id=%d ",getpid(),this,id);
00560   if(s->mode&WAIT) wait.Broadcast();
00561   if(result) {
00562     if(store && storeSize>0)
00563       memcpy(&stores[(id-1)*storeSize],store,storeSize);
00564     s->mode=GOOD;
00565     LBPUT("(GOOD)");
00566     return 0;
00567     }
00568   else {
00569     int m=s->mode&MASK;
00570     if(m==GOOD)
00571       m=s->mode=FREE;
00572     if(m==FREE || (m>=FAIL1 && m<=FAIL2)) {
00573       s->mode=(s->mode&MASK)+1;
00574       if(s->mode<maxFail)
00575         LBPUT("(FAIL%d)",s->mode);
00576       else
00577         m=s->mode=FAILN;
00578       }
00579     if(m==FAILN)
00580       LBPUT("(FAILN)");
00581     return s->mode;
00582     }
00583   LBEND();
00584 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines