📄 smartcard.c
字号:
/* * Softcam plugin to VDR (C++) * * This code is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This code is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * Or, point your browser to http://www.gnu.org/copyleft/gpl.html */#include <stdlib.h>#include <stdio.h>#include <string.h>#include <stdarg.h>#include <errno.h>#include <fcntl.h>#include <sys/ioctl.h>#include <sys/poll.h>#include <unistd.h>#include <termios.h>#include <linux/serial.h>#include <ctype.h>#include <vdr/tools.h>#include <vdr/thread.h>#include "smartcard.h"#include "misc.h"#include "log-core.h"#define DATAFILE "smartcard.conf"#define ISO_FREQ 3571200 // Hz//#define SER_EMU // use serial emulation (select one of the following)//#define EMU_SECA // fake Seca card//#define EMU_IRD // fake Irdeto/Beta card//#define EMU_IRD_384 // fake ACS 384, if undefined ACS 383//#define EMU_CRYPTO // fake Cryptoworks card//#define NO_PTS_PROTO // disable PTS protocol (baudrate changes)struct BaudRates { int real; speed_t apival; };static const struct BaudRates BaudRateTab[] = { { 9600, B9600 }, { 19200, B19200 }, { 38400, B38400 }, { 57600, B57600 }, { 115200, B115200 }, { 230400, B230400 } };// -- cSerial ------------------------------------------------------------------class cSerial {private: char *devName; int fd; int currMode, statInv; bool invRST; // speed_t FindBaud(int baud);#ifdef SER_EMU unsigned char devBuff[1024], nextSW[SB_LEN]; int buffCount, dataLen, record; bool cmdStart, rts, dsr, flag; int remTime;#endifpublic: cSerial(const char *DevName, bool invCD, bool InvRST); ~cSerial(); bool Open(void); bool SetMode(int mode, int baud=9600); int CurrentMode(void) const { return currMode; } void Flush(void); int Read(unsigned char *mem, int len, int timeout, int initialTimeout=0); int Write(const unsigned char *mem, int len, int delay=0); void ToggleRTS(void); bool CheckCAR(void); void Close(void); const char *DeviceName(void) const { return devName; } };cSerial::cSerial(const char *DevName, bool invCD, bool InvRST){ devName=strdup(DevName); statInv=invCD ? TIOCM_CAR:0; invRST=InvRST; fd=-1; currMode=SM_NONE;}cSerial::~cSerial(){ Close(); free(devName);}#ifndef SER_EMUbool cSerial::Open(void){ PRINTF(L_CORE_SERIAL,"%s: open serial port",devName); fd=open(devName,O_RDWR|O_NONBLOCK|O_NOCTTY); if(fd>=0) { PRINTF(L_CORE_SERIAL,"%s: set DTR/RTS",devName); unsigned int modembits; modembits=TIOCM_DTR; CHECK(ioctl(fd, TIOCMBIS, &modembits)); modembits=TIOCM_RTS; CHECK(ioctl(fd, invRST?TIOCMBIS:TIOCMBIC, &modembits)); PRINTF(L_CORE_SERIAL,"%s: init done",devName); return true; } else PRINTF(L_GEN_ERROR,"%s: open failed: %s",devName,strerror(errno)); return false;}void cSerial::Close(void){ if(fd>=0) { PRINTF(L_CORE_SERIAL,"%s: shutting down",devName); Flush(); unsigned int modembits=0; CHECK(ioctl(fd,TIOCMSET,&modembits)); close(fd); fd=-1; PRINTF(L_CORE_SERIAL,"%s: shutdown done",devName); }}speed_t cSerial::FindBaud(int baud){ for(int i=0; i<(int)(sizeof(BaudRateTab)/sizeof(struct BaudRates)); i++) { int b=BaudRateTab[i].real; int d=((b-baud)*10000)/b; if(abs(d)<=300) { PRINTF(L_CORE_SERIAL,"%s: requested baudrate %d -> %d (%+.2f%%)",devName,baud,b,(float)d/100.0); return BaudRateTab[i].apival; } } PRINTF(L_CORE_SERIAL,"%s: requested baudrate %d -> custom",devName,baud); return B0;}bool cSerial::SetMode(int mode, int baud){ if(fd>=0) { speed_t bconst=FindBaud(baud); bool custom=false; if(bconst==B0) { custom=true; bconst=B38400; } struct termios tio; memset(&tio,0,sizeof(tio)); LBSTARTF(L_CORE_SERIAL); LBPUT("%s: set serial options: %d,",devName,baud); tio.c_cflag = (CS8 | CREAD | HUPCL | CLOCAL); if(!(mode&SM_1SB)) tio.c_cflag |= CSTOPB; tio.c_iflag = (INPCK | BRKINT); tio.c_cc[VMIN] = 1; cfsetispeed(&tio,bconst); cfsetospeed(&tio,bconst); switch(mode&SM_MASK) { case SM_8E2: LBPUT("8e%d",(mode&SM_1SB)?1:2); tio.c_cflag |= PARENB; break; case SM_8N2: LBPUT("8n%d",(mode&SM_1SB)?1:2); break; case SM_8O2: LBPUT("8o%d",(mode&SM_1SB)?1:2); tio.c_cflag |= (PARENB | PARODD); break; default: LBPUT("BAD MODE"); return false; } LBEND(); struct serial_struct s; if(ioctl(fd,TIOCGSERIAL,&s)<0) { PRINTF(L_GEN_ERROR,"%s: get serial failed: %s",devName,strerror(errno)); return false; } if(!custom && ((s.flags&ASYNC_SPD_MASK)==ASYNC_SPD_CUST || s.custom_divisor!=0)) { s.custom_divisor=0; s.flags &= ~ASYNC_SPD_MASK; if(ioctl(fd,TIOCSSERIAL,&s)<0) { PRINTF(L_GEN_ERROR,"%s: set serial failed: %s",devName,strerror(errno)); return false; } } if(!tcsetattr(fd,TCSANOW,&tio)) { if(custom) { s.custom_divisor=(s.baud_base+(baud/2))/baud; s.flags=(s.flags&~ASYNC_SPD_MASK) | ASYNC_SPD_CUST; PRINTF(L_CORE_SERIAL,"%s: custom: baud_base=%d baud=%d devisor=%d -> effective baudrate %d (%+.2f%% off)",devName,s.baud_base,baud,s.custom_divisor,s.baud_base/s.custom_divisor,(float)(s.baud_base/s.custom_divisor-baud)/(float)baud); if(ioctl(fd,TIOCSSERIAL,&s)<0) { PRINTF(L_GEN_ERROR,"%s: set serial failed: %s",devName,strerror(errno)); return false; } } currMode=mode; Flush(); return true; } else PRINTF(L_GEN_ERROR,"%s: tcsetattr failed: %s",devName,strerror(errno)); } return false;}void cSerial::Flush(void){ if(fd>=0) CHECK(tcflush(fd,TCIOFLUSH));}int cSerial::Read(unsigned char *mem, int len, int timeout, int initialTimeout){ PRINTF(L_CORE_SERIAL,"%s: read len=%d timeout=%d:%d",devName,len,timeout,initialTimeout); bool incomplete=false; if(len<0) { len=-len; incomplete=true; } int to=initialTimeout>0 ? initialTimeout : timeout; int n=0; while(n<len) { int r=read(fd,mem+n,len-n); if(r<=0) { if(r==0) PRINTF(L_CORE_SERIAL,"%s: read bogus eof",devName); if(errno==EAGAIN) { struct pollfd u; u.fd=fd; u.events=POLLIN; r=poll(&u,1,to); if(r<0) { PRINTF(L_GEN_ERROR,"%s: read poll failed: %s",devName,strerror(errno)); return -1; } else if(r==0) { // timeout PRINTF(L_CORE_SERIAL,"%s: read timeout (%d ms)",devName,to); if(n>0 && incomplete) break; // return bytes read so far return -2; } continue; } else { PRINTF(L_GEN_ERROR,"%s: read failed: %s",devName,strerror(errno)); return -1; } } n+=r; to=timeout; } HEXDUMP(L_CORE_SERIAL,mem,n,"%s: read data",devName); return n;}int cSerial::Write(const unsigned char *mem, int len, int delay){ PRINTF(L_CORE_SERIAL,"%s: write len=%d delay=%d",devName,len,delay); HEXDUMP(L_CORE_SERIAL,mem,len,"%s: write data",devName); Flush(); int n=0; while(n<len) { struct pollfd u; u.fd=fd; u.events=POLLOUT; int r; do { r=poll(&u,1,500); if(r<0) { PRINTF(L_GEN_ERROR,"%s: write poll failed: %s",devName,strerror(errno)); return -1; } else if(r==0) { // timeout PRINTF(L_CORE_SERIAL,"%s: write timeout",devName); return -2; } } while(r<1); r=write(fd,mem+n,delay>0?1:len-n); if(r<0 && errno!=EAGAIN) { PRINTF(L_GEN_ERROR,"%s: write failed: %s",devName,strerror(errno)); return -1; } if(r>0) n+=r; if(delay>0) cCondWait::SleepMs(delay); } return n;}void cSerial::ToggleRTS(void){ int mask=0; if(ioctl(fd,TIOCMGET,&mask)<0) { LOG_ERROR; return; } if(mask&TIOCM_RTS) { mask&=~TIOCM_RTS; PRINTF(L_CORE_SERIAL,"%s: toggle RTS, now off",devName); } else { mask|= TIOCM_RTS; PRINTF(L_CORE_SERIAL,"%s: toggle RTS, now on",devName); } CHECK(ioctl(fd,TIOCMSET,&mask)); Flush();}bool cSerial::CheckCAR(void){ int status=0; if(ioctl(fd,TIOCMGET,&status)<0) { LOG_ERROR; return false; } PRINTF(L_CORE_SERIAL,"%s: CAR is %sactive (lines:%s%s%s%s%s%s%s%s%s)", devName,(status&TIOCM_CAR)?"in":"", (status&TIOCM_LE)?" LE":"", (status&TIOCM_DTR)?" DTR":"", (status&TIOCM_RTS)?" RTS":"",(status&TIOCM_ST)?" ST":"", (status&TIOCM_SR)?" SR":"", (status&TIOCM_CTS)?" CTS":"", (status&TIOCM_CAR)?" CAR":"",(status&TIOCM_RNG)?" RNG":"", (status&TIOCM_DSR)?" DSR":"" ); status^=statInv; // invert status for broken cardreaders return !(status & TIOCM_CAR);}#else //SER_EMU//// emulator for a serial phoenix interface// (activate in smartcard.h)//bool cSerial::Open(void){ PRINTF(L_CORE_SERIAL,"serial: open serial port %s (emulator)",devName); PRINTF(L_CORE_SERIAL,"serial: init done"); fd=1; buffCount=0; cmdStart=true; rts=false; flag=false; remTime=time(0); dsr=true; return true;}void cSerial::Close(void){ if(fd>=0) { PRINTF(L_CORE_SERIAL,"serial: shutting down ... "); fd=-1; PRINTF(L_CORE_SERIAL,"done"); }}speed_t cSerial::FindBaud(int baud){ return B0;}bool cSerial::SetMode(int mode, int baud){ currMode=mode; Flush(); return true;}void cSerial::Flush(void){ buffCount=0;}#define PUSH(mem,len) { memcpy(devBuff+buffCount,(mem),(len)); buffCount+=(len); }#define PUSHB(b) { devBuff[buffCount++]=(b); }int cSerial::Read(unsigned char *mem, int len, int timeout, int initialTimeout){ PRINTF(L_CORE_SERIAL,"serial: read len=%d timeout=%d",len,timeout); if(len<0) { len=-len; if(buffCount<len) len=buffCount; } if(buffCount<len) return -2; // timeout memcpy(mem,devBuff,len); memmove(devBuff,devBuff+len,buffCount-len); buffCount-=len; return len;}int cSerial::Write(const unsigned char *mem, int len, int delay){ PRINTF(L_CORE_SERIAL,"serial: write len=%d delay=%d",len,delay); Flush(); cCondWait::SleepMs(300); PUSH(mem,len); // echo back#if defined(EMU_SECA) if(cmdStart && len==CMD_LEN) { // new INS PUSHB(mem[INS_IDX]); // ACK byte cmdStart=false; dataLen=mem[LEN_IDX]; unsigned char data[256]; memset(data,0,sizeof(data)); nextSW[0]=0x90; nextSW[1]=0x00; bool readcmd=false; switch(mem[0]*256+mem[1]) { case 0xc13a: case 0xc10e: readcmd=true; break; case 0xc116: data[3]=0x04; readcmd=true; break; case 0xc112: data[1]=0x64; data[22]=0x1d; data[23]=0x84; readcmd=true; break; case 0xc132: data[1]=0xFF; data[2]=0xFF; data[3]=0xFF; data[4]=0xFF; data[5]=0xFF; data[6]=0xFF; data[7]=0xFF; data[8]=0xFF; readcmd=true; break; } if(readcmd) { PUSH(data,dataLen); PUSH(nextSW,2); cmdStart=true; } } else { dataLen-=len; if(dataLen<=0 && !cmdStart) { PUSH(nextSW,2); cmdStart=true; } }#elif defined(EMU_CRYPTO) if(cmdStart && len==CMD_LEN) { // new INS cmdStart=false; dataLen=mem[LEN_IDX]; unsigned char data[256]; memset(data,0,sizeof(data)); nextSW[0]=0x90; nextSW[1]=0x00; bool readcmd=false; switch(mem[0]*256+mem[1]) { case 0xa4a4: switch(mem[5]*256+mem[6]) { case 0x2f01: case 0x0f00: case 0x0f20: //case 0x0f40: case 0x0f60: case 0x0e11: case 0x1f88: case 0x1f8c: nextSW[0]=0x9f; nextSW[1]=0x11; break; default: nextSW[0]=0x94; nextSW[1]=0x04; break; } break; case 0xa4a2: if(mem[2]==1) { if(mem[4]==3) { record=0x01; nextSW[0]=0x9f; nextSW[1]=0x26; } else { record=0x02; nextSW[0]=0x9f; nextSW[1]=0x42; } } else { record=mem[5]; switch(record) { case 0x80: nextSW[0]=0x9f; nextSW[1]=0x07; break; case 0xD1: nextSW[0]=0x9f; nextSW[1]=0x04; break; case 0x9F: nextSW[0]=0x9f; nextSW[1]=0x03; break; case 0x9E: nextSW[0]=0x9f; nextSW[1]=0x42; break; case 0xD6: case 0xC0: nextSW[0]=0x9f; nextSW[1]=0x12; break; default: nextSW[0]=0x94; nextSW[1]=0x02; break; } } break; case 0xa4b2: readcmd=true; switch(record) { case 0x01: if(mem[3]==1) { nextSW[0]=0x94; nextSW[1]=0x02; dataLen=0; } else { dataLen=0x26; static const unsigned char resp[0x26] = { 0x83,0x01,0x88, 0x8c,0x03,0x40,0x30,0x40, 0x8d,0x04,0x16,0xac,0x16,0xba, 0xd5,0x10,'D','u','m','m','y',0,0,0,0,0,0,0,0x12,0x34,0x56,0x67, 0x8f,0x01,0x55, 0x91,0x01,0x40 }; memcpy(data,resp,sizeof(resp)); } break; case 0x02:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -