// initialy written by anetwolf anetwolf@hotmail.com
// based on crypto & protocol information from _silencer
+// EMM handling based on contribution from appiemulder
#include <stdio.h>
#include <stdlib.h>
#include <openssl/sha.h>
#include "cc.h"
+#include "parse.h"
#include "helper.h"
#include "version.h"
class cShares;
-class cShare : public cSimpleItem {
+class cShare : public cSimpleItem, public cIdSet {
friend class cShares;
private:
int shareid, caid;
cSimpleList<cShareProv> prov;
int hops, lag;
int status;
+ unsigned char ua[8];
+ bool emmready;
+ cMsgCache *cache;
+ //
+ void Init(void);
+ void SetCache(void);
public:
- cShare(int Shareid, int Caid, int Hops);
+ cShare(int Shareid, int Caid, int Hops, const unsigned char *Ua);
cShare(const cShare *s);
+ ~cShare();
bool UsesProv(void) const;
bool HasProv(int provid) const;
- void AddProv(int provid);
+ bool CheckAddProv(const unsigned char *prov, const unsigned char *sa);
bool Compare(const cShare *s) const;
int ShareID(void) const { return shareid; };
int CaID(void) const { return caid;};
int Hops(void) const { return hops; };
int Lag(void) const { return lag; };
int Status(void) const { return status; }
+ bool EmmReady(void) const { return emmready; }
+ cMsgCache *Cache(void) const { return cache; }
};
-cShare::cShare(int Shareid, int Caid, int Hops)
+cShare::cShare(int Shareid, int Caid, int Hops, const unsigned char *Ua)
{
shareid=Shareid;
caid=Caid;
hops=Hops;
lag=STDLAG; status=0;
+ memcpy(ua,Ua,sizeof(ua));
+ Init();
}
cShare::cShare(const cShare *s)
hops=s->hops;
lag=s->lag;
status=s->status;
+ memset(ua,0,sizeof(ua));
+ Init();
+}
+
+cShare::~cShare()
+{
+ delete cache;
+}
+
+void cShare::Init(void)
+{
+ cache=0; emmready=false;
+ if(!CheckNull(ua,sizeof(ua)))
+ switch(caid>>8) {
+ case 0x17:
+ case 0x06: SetCard(new cCardIrdeto(ua[4],&ua[5])); emmready=true; break;
+ case 0x01: SetCard(new cCardSeca(&ua[2])); emmready=true; break;
+ case 0x0b: SetCard(new cCardConax(&ua[1])); emmready=true; break;
+ case 0x09: SetCard(new cCardNDS(&ua[4])); emmready=true; break;
+ case 0x05: SetCard(new cCardViaccess(&ua[3])); emmready=true; break;
+ case 0x0d: SetCard(new cCardCryptoworks(&ua[3])); emmready=true; break;
+ case 0x12:
+ case 0x18: if((caid>>8)==0x18 || caid==0x1234) {
+ SetCard(new cCardNagra2(&ua[4]));
+ emmready=true;
+ break;
+ }
+ // fall through
+ default: PRINTF(L_CC_CCCAM2,"share %08x: can't handle unique updates for CAID %04x",shareid,caid);
+ break;
+ }
+ SetCache();
+}
+
+void cShare::SetCache(void)
+{
+ if(emmready) {
+ if(!cache) cache=new cMsgCache(32,0);
+ if(!cache) {
+ emmready=false;
+ PRINTF(L_CC_CCCAM2,"share %08x: failed to alloc EMM cache",shareid);
+ }
+ }
+ else {
+ delete cache; cache=0;
+ }
}
bool cShare::UsesProv(void) const
return false;
}
-void cShare::AddProv(int provid)
+bool cShare::CheckAddProv(const unsigned char *pr, const unsigned char *sa)
{
- if(!HasProv(provid)) {
+ unsigned char s[8];
+ memset(&s[0],0,4); memcpy(&s[4],sa,4);
+ int provid=UINT32_BE(pr-1)&0xFFFFFF;
+ bool res=false;
+ if(!CheckNull(s,sizeof(s)))
+ switch(caid>>8) {
+ case 0x17:
+ case 0x06: AddProv(new cProviderIrdeto(s[4],&s[5])); res=true; break;
+ case 0x01: AddProv(new cProviderSeca(&pr[1],&s[4])); res=true; break;
+ case 0x0b: AddProv(new cProviderConax(&s[1])); res=true; break;
+ case 0x09: AddProv(new cProviderNDS(&s[4])); res=true; break;
+ case 0x05: AddProv(new cProviderViaccess(&pr[0],&s[4])); res=true; break;
+ case 0x0d: AddProv(new cProviderCryptoworks(&s[3])); res=true; break;
+ default: PRINTF(L_CC_CCCAM2,"share %08x: can't handle shared updates for CAID %04x",shareid,caid);
+ break;
+ }
+ if(res) emmready=true;
+ SetCache();
+
+ if(UsesProv() && !HasProv(provid)) {
cShareProv *sp=new cShareProv;
sp->provid=provid;
prov.Add(sp);
+ res=true;
}
+ return res;
}
bool cShare::Compare(const cShare *s) const
unsigned char data[0];
} __attribute__((packed));
+struct EmmRequest {
+ struct CmdHeader header;
+ unsigned short caid; // BE
+ unsigned char dummy; //XXX what is that?
+ unsigned int provid; // BE
+ unsigned int shareid; // BE
+ unsigned char datalen;
+ unsigned char data[0];
+ } __attribute__((packed));
+
struct DcwAnswer {
struct CmdHeader header;
unsigned char cw[16];
unsigned char count;
struct {
unsigned char provid[3];
- unsigned char info[4];
+ unsigned char sa[4];
} prov[0];
} __attribute__((packed));
unsigned char nodeid[8];
int shareid;
char username[21], password[64];
- bool login;
+ bool login, emmProcessing;
cTimeMs lastsend;
//
bool newcw;
virtual bool Init(const char *CfgDir);
virtual bool CanHandle(unsigned short SysId);
virtual bool ProcessECM(const cEcmInfo *ecm, const unsigned char *data, unsigned char *Cw, int cardnum);
+ virtual bool ProcessEMM(int caSys, const unsigned char *data);
};
static cCardClientLinkReg<cCardClientCCcam2> __ncd("cccam2");
:cCardClient(Name)
,cThread("CCcam2 reader")
{
- shareid=0; readerTid=0; newcw=login=false;
+ shareid=0; readerTid=0; newcw=login=emmProcessing=false;
so.SetRWTimeout(10*1000);
}
decr.Decrypt(tempcw,tempcw,16);
break;
}
+ case 2:
+ PRINTF(L_CC_CCCAM2EX,"EMM ack");
+ break;
case 4:
{
struct DelShare *del=(struct DelShare *)hdr;
int shareid=UINT32_BE(&del->shareid);
+ bool check=false;
shares.Lock(true);
for(cShare *s=shares.First(); s;) {
cShare *n=shares.Next(s);
if(s->ShareID()==shareid) {
PRINTF(L_CC_CCCAM2SH,"REMOVE share %08x caid: %04x (count %d)",s->ShareID(),s->CaID(),shares.Count());
int caid=s->CaID();
+ if(s->EmmReady()) check=true;
shares.Del(s);
if(!shares.HasCaid(caid)) CaidsChanged();
}
s=n;
}
+ if(check && emmProcessing) {
+ bool emm=false;
+ for(cShare *s=shares.First(); s; s=shares.Next(s))
+ if(s->EmmReady()) { emm=true; break; }
+ if(!emm) {
+ emmProcessing=false;
+ PRINTF(L_CC_CCCAM2,"disabled EMM processing");
+ }
+ }
shares.Unlock();
break;
}
shares.Lock(false);
if(!shares.HasCaid(caid)) CaidsChanged();
shares.Unlock();
- cShare *s=new cShare(shareid,caid,add->uphops);
+ cShare *s=new cShare(shareid,caid,add->uphops,add->cardserial);
LBSTARTF(L_CC_CCCAM2SH);
- LBPUT("ADD share %08x hops %d maxdown %d caid %04x serial ",shareid,add->uphops,add->maxdown,caid);
+ LBPUT("ADD share %08x hops %d maxdown %d caid %04x ua ",shareid,add->uphops,add->maxdown,caid);
for(int i=0; i<8; i++) LBPUT("%02x",add->cardserial[i]);
struct ProvInfo *prov=(struct ProvInfo *)(add+1);
- if(s->UsesProv() && prov->count>0) {
- LBPUT(" prov");
+ if(prov->count>0) {
+ bool first=true;
for(int i=0; i<prov->count; i++) {
- int provider=UINT32_BE(prov->prov[i].provid-1)&0xFFFFFF;
- s->AddProv(provider);
- LBPUT(" %06x",provider);
+ if(s->CheckAddProv(prov->prov[i].provid,prov->prov[i].sa)) {
+ if(first) { LBPUT(" prov"); first=false; }
+ LBPUT(" %06x/%08x",UINT32_BE(prov->prov[i].provid-1)&0xFFFFFF,UINT32_BE(prov->prov[i].sa));
+ }
}
}
+ if(s->EmmReady()) LBPUT(" (EMM)");
LBEND();
- shares.Lock(true); shares.Add(s); shares.Unlock();
+ shares.Lock(true);
+ shares.Add(s);
+ if(s->EmmReady() && !emmProcessing && emmAllowed) {
+ emmProcessing=true;
+ PRINTF(L_CC_CCCAM2,"enabled EMM processing");
+ }
+ shares.Unlock();
break;
}
case 8:
Cancel(cThread::ThreadId()!=readerTid ? 2:-1);
readerTid=0;
cCardClient::Logout();
+ shares.Lock(true);
+ shares.Clear();
+ emmProcessing=false;
+ shares.Unlock();
}
bool cCardClientCCcam2::Login(void)
{
Logout();
- shares.Lock(true);
- shares.Clear();
- shares.Unlock();
if(!so.Connect(hostname,port)) return false;
so.SetQuietLog(true);
return false;
}
+bool cCardClientCCcam2::ProcessEMM(int caSys, const unsigned char *data)
+{
+ bool res=false;
+ if(emmProcessing && emmAllowed) {
+ cMutexLock lock(this);
+ shares.Lock(false);
+ for(cShare *s=shares.First(); s; s=shares.Next(s)) {
+ if(s->EmmReady() && s->CaID()==caSys) {
+ cProvider *p;
+ cAssembleData ad(data);
+ if(s->MatchAndAssemble(&ad,0,&p)) {
+ const unsigned char *d;
+ while((d=ad.Assembled())) {
+ int len=SCT_LEN(d);
+ int id=s->Cache()->Get(d,len,0);
+ if(id>0) {
+ if(len<256) {
+ unsigned char bb[sizeof(struct EmmRequest)+256];
+ struct EmmRequest *req=(struct EmmRequest *)bb;
+ int emm_len=sizeof(struct EmmRequest)+len;
+ memset(req,0,sizeof(struct EmmRequest));
+ memcpy(req+1,d,len);
+ req->header.cmd=2;
+ SETCMDLEN(&req->header,emm_len);
+ BYTE2_BE(&req->caid,s->CaID());
+ BYTE4_BE(&req->provid,p ? p->ProvId() : 0);
+ BYTE4_BE(&req->shareid,s->ShareID());
+ req->datalen=len;
+ if(!CryptSend((unsigned char *)req,emm_len))
+ PRINTF(L_CC_CCCAM2,"failed to send emm request");
+ }
+ else PRINTF(L_CC_CCCAM2,"EMM data length >=256 not supported by CCcam");
+ s->Cache()->Cache(id,true,0);
+ }
+ }
+ res=true;
+ }
+ }
+ }
+ shares.Unlock();
+ }
+ return res;
+}
+
void cCardClientCCcam2::Action(void)
{
readerTid=cThread::ThreadId();