/* Compile as follows:
* gcc -O -fbuiltin -fomit-frame-pointer -fPIC -shared -o ca.so ca.c -ldl
*
* run cccam with:
* LD_PRELOAD=./ca.so ./xxx.i386
*
* Will then send CWs to vdr-sc cardclient cccam
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
//#define DEBUG
#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
#include <dlfcn.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/dvb/ca.h>
#include <linux/dvb/dmx.h>
#include <linux/dvb/frontend.h>
#include <linux/ioctl.h>
#include <stdio.h>
#include <time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/un.h>
#include <syslog.h>
#include <errno.h>
#if defined(RTLD_NEXT)
#define REAL_LIBC RTLD_NEXT
#else
#define REAL_LIBC ((void *) -1L)
#endif
#define PORT 9000
#define MAX_CA 4
static int cafd[MAX_CA] = {-1,-1,-1,-1};
static int cafdc[MAX_CA] = {0,0,0,0};
#define MAX_INDEX 64
#define KEY_SIZE 8
#define INFO_SIZE (2+KEY_SIZE+KEY_SIZE)
#define EVEN_OFF (2)
#define ODD_OFF (2+KEY_SIZE)
static unsigned char ca_info[MAX_CA][MAX_INDEX][INFO_SIZE];
#define CA_BASE "/dev/dvb/adapter0/ca"
#define DEMUX_BASE "/dev/dvb/adapter0/demux"
#define DEMUX_MAP "/dev/dvb/adapter%d/demux0"
#define CPUINFO_BASE "/proc/cpuinfo"
#define CPUINFO_MAP "/tmp/cpuinfo"
//#define DBG(x...) {syslog(LOG_INFO, x);}
#define DBG(x...)
#define INF(x...) {syslog(LOG_INFO, x);}
#define ERR(x...) {syslog(LOG_ERR, x);}
int proginfo=0;
#define PROGINFO() {if(!proginfo){syslog(LOG_INFO,"ca.so for cccam V1.09");proginfo=1;}}
int sendcw(int fd, unsigned char *buff) {
DBG(">>>>>>>> sendcw %02x%02x %02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x%02x%02x%02x%02x", buff[0], buff[1], buff[2], buff[3], buff[4], buff[5], buff[6], buff[7], buff[8], buff[9], buff[10], buff[11], buff[12], buff[13], buff[14], buff[15], buff[16], buff[17], buff[18]);
if ( send(fd,buff, 18, 0) == -1 ) {
ERR("ca.so: Send cw failed %d", errno);
return 0;
} // if
return 1;
} // sendcw
static int cactl (int fd, int cai, int request, void *argp) {
ca_descr_t *ca = (ca_descr_t *)argp;
ca_pid_t *cpd = (ca_pid_t *)argp;
switch (request) {
case CA_SET_DESCR:
DBG(">>>>>>>> cactl CA_SET_DESCR fd %d cai %d req %d par %d idx %d %02x%02x%02x%02x%02x%02x%02x%02x", fd, cai, request, ca->parity, ca->index, ca->cw[0], ca->cw[1], ca->cw[2], ca->cw[3], ca->cw[4], ca->cw[5], ca->cw[6], ca->cw[7]);
if(ca->parity==0)
memcpy(&ca_info[cai][ca->index][EVEN_OFF],ca->cw,KEY_SIZE); // even key
else if(ca->parity==1)
memcpy(&ca_info[cai][ca->index][ODD_OFF],ca->cw,KEY_SIZE); // odd key
else
ERR("ca.so: Invalid parity %d in CA_SET_DESCR for ca id %d", ca->parity, cai);
sendcw(fd, ca_info[cai][ca->index]);
break;
case CA_SET_PID:
DBG(">>>>>>>> cactl CA_SET_PID fd %d cai %d req %d (%d %04x)", fd, cai, request, cpd->index, cpd->pid);
if (cpd->index >=0 && cpd->index < MAX_INDEX) {
ca_info[cai][cpd->index][0] = (cpd->pid >> 0) & 0xff;
ca_info[cai][cpd->index][1] = (cpd->pid >> 8) & 0xff;
} else if (cpd->index == -1) {
memset(&ca_info[cai], 0, sizeof(ca_info[cai]));
} else
ERR("ca.so: Invalid index %d in CA_SET_PID (%d) for ca id %d", cpd->index, MAX_INDEX, cai);
return 1;
case CA_RESET:
DBG(">>>>>>>> cactl CA_RESET cai %d", cai);
break;
case CA_GET_CAP:
DBG(">>>>>>>> cactl CA_GET_CAP cai %d", cai);
break;
case CA_GET_SLOT_INFO:
DBG(">>>>>>>> cactl CA_GET_SLOT_INFO cai %d", cai);
break;
default:
DBG(">>>>>>>> cactl unhandled req %d cai %d", request, cai);
//errno = EINVAL;
//return -1;
} // switch
return 0;
} // cactl
/*
CPU Model
Brcm4380 V4.2 // DM8000
Brcm7401 V0.0 // DM800
MIPS 4KEc V4.8 // DM7025
*/
static const char *cpuinfo_file = "\
system type : ATI XILLEON HDTV SUPERTOLL\n\
processor : 0\n\
cpu model : Brcm4380 V4.2\n\
BogoMIPS : 297.98\n\
wait instruction : yes\n\
microsecond timers : yes\n\
tlb_entries : 16\n\
extra interrupt vector : yes\n\
hardware watchpoint : yes\n\
VCED exceptions : not available\n\
VCEI exceptions : not available\n\
";
int write_cpuinfo (void) {
static FILE* (*func) (const char *, const char *) = NULL;
func = (FILE* (*) (const char *, const char *)) dlsym (REAL_LIBC, "fopen");
FILE* file = func(CPUINFO_MAP, "w");
if(file == NULL) {
printf("error \"%s\" opening file\n", strerror(errno));
return -1;
}
int ret = fwrite(cpuinfo_file, strlen(cpuinfo_file), 1, file);
fclose(file);
return 0;
}
FILE *fopen(const char *path, const char *mode){
static FILE* (*func) (const char *, const char *) = NULL;
write_cpuinfo();
if (!func) func = (FILE* (*) (const char *, const char *)) dlsym (REAL_LIBC, "fopen");
if (!strcmp(path, CPUINFO_BASE)) {
INF("ca.so fopen %s mapped to %s", path, CPUINFO_MAP);
return (*func) (CPUINFO_MAP, mode);
} // if
DBG(">>>>>>>> fopen %s", path);
return (*func) (path, mode);
} // fopen
int open (const char *pathname, int flags, ...) {
static int (*func) (const char *, int, mode_t) = NULL;
if (!func) func = (int (*) (const char *, int, mode_t)) dlsym (REAL_LIBC, "open");
PROGINFO();
va_list args;
mode_t mode;
va_start (args, flags);
mode = va_arg (args, mode_t);
va_end (args);
if(strstr(pathname, CA_BASE)) {
int cai=pathname[strlen(CA_BASE)]-'0';
if((cai >= 0) && (cai < MAX_CA)) {
DBG(">>>>>>>> open cafd[%d] flags %d %s (%d)", cai, flags, pathname, cafdc[cai]);
if(cafd[cai]==-1) {
cafd[cai] = socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
if(cafd[cai]==-1) {
ERR("Failed to open socket (%d)", errno);
} else {
struct sockaddr_in saddr;
fcntl(cafd[cai],F_SETFL,O_NONBLOCK);
bzero(&saddr,sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(PORT);
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
int r = connect(cafd[cai], (struct sockaddr *) &saddr, sizeof(saddr));
if (r<0) {
ERR("Failed to connect socket (%d)", errno);
close(cafd[cai]);
cafd[cai]=-1;
} // if
} // if
} // if
if(cafd[cai]!=-1)
cafdc[cai]++;
else
cafdc[cai] = 0;
return (cafd[cai]);
} // if
} else if (strstr(pathname, DEMUX_BASE)) {
int cai=pathname[strlen(DEMUX_BASE)]-'0';
if((cai >= 0) && (cai < MAX_CA)) {
char dest[256];
sprintf(dest, DEMUX_MAP, cai);
DBG(">>>>>>>> open %s mapped to %s", pathname, dest);
return (*func) (dest, flags, mode);
} // if
} // if
DBG(">>>>>>>> open %s", pathname);
return (*func) (pathname, flags, mode);
} // open
int ioctl (int fd, int request, void *a) {
static int (*func) (int, int, void *) = NULL;
if (!func) func = (int (*) (int, int, void *)) dlsym (REAL_LIBC, "ioctl");
int i;
for(i=0;i<MAX_CA;i++)
if (fd == cafd[i])
return cactl (fd, i, request, a);
return (*func) (fd, request, a);
} // ioctl
int close (int fd) {
static int (*func) (int) = NULL;
if (!func) func = (int (*) (int)) dlsym (REAL_LIBC, "close");
int i;
for(i=0;i<MAX_CA;i++) {
if (fd == cafd[i]) {
DBG(">>>>>>>> close cafd[%d] (%d)", i, cafdc[i]);
if(--cafdc[i]) return 0;
cafd[i] = -1;
} // if
} // for
return (*func) (fd);
} // close
#endif