static int32_t camd33_send(uchar *buf, int32_t ml) { int32_t l; if(!cur_client()->pfd) { return (-1); } l = boundary(4, ml); memset(buf + ml, 0, l - ml); cs_log_dump_dbg(D_CLIENT, buf, l, "send %d bytes to client", l); if(cur_client()->crypted) { aes_encrypt_idx(cur_client()->aes_keys, buf, l); } return (send(cur_client()->pfd, buf, l, 0)); }
static int32_t __camd35_send(struct s_client *cl, uchar *buf, int32_t buflen, int answer_awaited) { int32_t l; unsigned char rbuf[REQ_SIZE + 15 + 4], *sbuf = rbuf + 4; if(!cl->udp_fd || !cl->crypted) { return (-1); } //exit if no fd or aes key not set! //Fix ECM len > 255 if(buflen <= 0) { buflen = ((buf[0] == 0) ? (((buf[21] & 0x0f) << 8) | buf[22]) + 3 : buf[1]); } l = 20 + (((buf[0] == 3) || (buf[0] == 4)) ? 0x34 : 0) + buflen; memcpy(rbuf, cl->ucrc, 4); memcpy(sbuf, buf, l); memset(sbuf + l, 0xff, 15); // set unused space to 0xff for newer camd3's i2b_buf(4, crc32(0L, sbuf + 20, buflen), sbuf + 4); l = boundary(4, l); cs_log_dump_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, sbuf, l, "send %d bytes to %s", l, username(cl)); aes_encrypt_idx(cl->aes_keys, sbuf, l); int32_t status; if(cl->is_udp) { status = sendto(cl->udp_fd, rbuf, l + 4, 0, (struct sockaddr *)&cl->udp_sa, cl->udp_sa_len); if(status == -1) { set_null_ip(&SIN_GET_ADDR(cl->udp_sa)); } } else { status = send(cl->udp_fd, rbuf, l + 4, 0); if(cl->typ == 'p' && cl->reader) { if(status == -1) { network_tcp_connection_close(cl->reader, "can't send"); } } else if(cl->typ == 'c') { if(status == -1) { cs_disconnect_client(cl); } } } if(status != -1) { if(cl->reader && answer_awaited) { cl->reader->last_s = time(NULL); } if(cl->reader && !answer_awaited) { cl->reader->last_s = cl->reader->last_g = time(NULL); } cl->last = time(NULL); } return status; }
static int32_t camd33_recv(struct s_client *client, uchar *buf, int32_t l) { int32_t n; if(!client->pfd) { return (-1); } if((n = cs_recv(client->pfd, buf, l, 0)) > 0) { client->last = time((time_t *) 0); if(client->crypted) { aes_encrypt_idx(cur_client()->aes_keys, buf, n); } } cs_log_dump_dbg(D_CLIENT, buf, n, "received %d bytes from client", n); return (n); }
void cardreader_process_ecm(struct s_reader *reader, struct s_client *cl, ECM_REQUEST *er) { cs_log_dump_dbg(D_ATR, er->ecm, er->ecmlen, "ecm:"); struct timeb tps, tpe; struct s_ecm_answer ea; memset(&ea, 0, sizeof(struct s_ecm_answer)); cs_ftime(&tps); int32_t rc = cardreader_do_ecm(reader, er, &ea); cs_ftime(&tpe); rdr_log_dbg(reader, D_READER, "%s: cardreader_do_ecm returned rc=%d (ERROR=%d)", __func__, rc, ERROR); ea.rc = E_FOUND; //default assume found ea.rcEx = 0; //no special flag if(rc == ERROR) { char buf[CS_SERVICENAME_SIZE]; rdr_log_dbg(reader, D_READER, "Error processing ecm for caid %04X, provid %06X, srvid %04X, servicename: %s", er->caid, er->prid, er->srvid, get_servicename(cl, er->srvid, er->prid, er->caid, buf, sizeof(buf))); ea.rc = E_NOTFOUND; ea.rcEx = 0; ICC_Async_DisplayMsg(reader, "Eer"); } if(rc == E_CORRUPT) { char buf[CS_SERVICENAME_SIZE]; rdr_log_dbg(reader, D_READER, "Error processing ecm for caid %04X, provid %06X, srvid %04X, servicename: %s", er->caid, er->prid, er->srvid, get_servicename(cl, er->srvid, er->prid, er->caid, buf, sizeof(buf))); ea.rc = E_NOTFOUND; ea.rcEx = E2_WRONG_CHKSUM; //flag it as wrong checksum memcpy(ea.msglog, "Invalid ecm type for card", 25); } write_ecm_answer(reader, er, ea.rc, ea.rcEx, ea.cw, ea.msglog, ea.tier, &ea.cw_ex); cl->lastecm = time((time_t *)0); char ecmd5[17 * 3]; cs_hexdump(0, er->ecmd5, 16, ecmd5, sizeof(ecmd5)); rdr_log_dbg(reader, D_READER, "ecm hash: %s real time: %"PRId64" ms", ecmd5, comp_timeb(&tpe, &tps)); reader_post_process(reader); }
static int32_t radegast_recv(struct s_client *client, uchar *buf, int32_t l) { int32_t n; if(!client->pfd) { return (-1); } if(client->typ == 'c') // server code { if((n = recv(client->pfd, buf, l, 0)) > 0) { client->last = time((time_t *) 0); } } else // client code { if((n = recv(client->pfd, buf, l, 0)) > 0) { cs_log_dump_dbg(D_CLIENT, buf, n, "radegast: received %d bytes from %s", n, remote_txt()); client->last = time((time_t *) 0); if((buf[0] == 0x02) && (buf[1] == 0x12) && (buf[2] == 0x05) && (buf[3] == 0x10)) { return (n); } // dcw received else if((buf[0] == 0x02) && (buf[1] == 0x02) && (buf[2] == 0x04) && (buf[3] == 0x00)) { return (n); } // dcw no found else if((buf[0] == 0x81) && (buf[1] == 0x00)) { return (n); } // cmd unknown else { n = -1; }// no cmd radegast disconnect } } return (n); }
static int32_t camd35_recv(uint8_t *buf, int32_t rs) { int32_t rc, s, n = 0, buflen = 0; for(rc = s = 0; !rc; s++) { switch(s) { case 0: if(rs < 36) { rc = -1; goto out; } break; case 1: switch(camd35_auth_client(buf)) { case 0: break; // ok case 1: rc = -2; break; // unknown user default: rc = -9; break; // error's from cs_auth() } memmove(buf, buf + 4, rs -= 4); break; case 2: aes_decrypt(&cl_aes_keys, buf, rs); if(rs != boundary(4, rs)) { cs_log_dbg(0, "WARNING: packet size has wrong decryption boundary"); } n = (buf[0] == 3) ? 0x34 : 0; //Fix for ECM request size > 255 (use ecm length field) if(buf[0] == 0) { buflen = (((buf[21] & 0x0f) << 8) | buf[22]) + 3; } else if(buf[0] == 0x3d || buf[0] == 0x3e || buf[0] == 0x3f) //cacheex-push { buflen = buf[1] | (buf[2] << 8); } else { buflen = buf[1]; } n = boundary(4, n + 20 + buflen); cs_log_dbg(0, "received %d bytes from client", rs); if(n < rs) { cs_log_dbg(0, "ignoring %d bytes of garbage", rs - n); } else if(n > rs) { rc = -3; } break; case 3: if(crc32(0, buf + 20, buflen) != b2i(4, buf + 4)) { rc = -4; cs_log_dump_dbg(buf, rs, "camd35 checksum failed for packet: "); cs_log_dbg(0, "checksum: %X", b2i(4, buf+4)); } if(!rc) { rc = n; } break; } } out: if((rs > 0) && ((rc == -1) || (rc == -2))) { cs_log_dbg(0, "received %d bytes from client (native)", rs); } switch(rc) { case -1: cs_log("packet is too small (received %d bytes, expected at least 36 bytes)", rs); break; case -2: cs_log("unknown user"); break; case -3: cs_log("incomplete request !"); break; case -4: cs_log("checksum error (wrong password ?)"); break; } return (rc); }
static int32_t camd35_recv(struct s_client *client, uchar *buf, int32_t l) { int32_t rc, s, rs, n = 0, buflen = 0, len = 0; for(rc = rs = s = 0; !rc; s++) { switch(s) { case 0: if(!client->udp_fd) { return (-9); } if(client->is_udp && client->typ == 'c') { rs = recv_from_udpipe(buf); } else { //read minimum packet size (4 byte ucrc + 32 byte data) to detect packet size (tcp only) //rs = cs_recv(client->udp_fd, buf, client->is_udp ? l : 36, 0); if(client->is_udp){ while (1){ rs = cs_recv(client->udp_fd, buf, l, 0); if (rs < 0){ if(errno == EINTR) { continue; } // try again in case of interrupt if(errno == EAGAIN) { continue; } //EAGAIN needs select procedure again cs_log_dbg(client->typ == 'c' ? D_CLIENT : D_READER, "ERROR: %s (errno=%d %s)", __func__, errno, strerror(errno)); break; }else {break;} } }else{ int32_t tot=36, readed=0; rs = 0; do { readed = cs_recv(client->udp_fd, buf+rs, tot, 0); if (readed < 0){ if(errno == EINTR) { continue; } // try again in case of interrupt if(errno == EAGAIN) { continue; } //EAGAIN needs select procedure again cs_log_dbg(client->typ == 'c' ? D_CLIENT : D_READER, "ERROR: %s (errno=%d %s)", __func__, errno, strerror(errno)); break; } if (readed == 0){ // nothing to read left! rc = -5; break; } if (readed > 0){ // received something, add it! tot-=readed; rs+=readed; } } while(tot!=0); } } if(rs < 36) { if(rc != -5) { rc = -1; } goto out; } break; case 1: switch(camd35_auth_client(client, buf)) { case 0: break; // ok case 1: rc = -2; break; // unknown user default: rc = -9; break; // error's from cs_auth() } memmove(buf, buf + 4, rs -= 4); break; case 2: aes_decrypt(client->aes_keys, buf, rs); if(rs != boundary(4, rs)) cs_log_dbg(client->typ == 'c' ? D_CLIENT : D_READER, "WARNING: packet size has wrong decryption boundary"); n = (buf[0] == 3) ? 0x34 : 0; //Fix for ECM request size > 255 (use ecm length field) if(buf[0] == 0) { buflen = (((buf[21] & 0x0f) << 8) | buf[22]) + 3; } else if(buf[0] == 0x3d || buf[0] == 0x3e || buf[0] == 0x3f) //cacheex-push { buflen = buf[1] | (buf[2] << 8); } else { buflen = buf[1]; } n = boundary(4, n + 20 + buflen); if(!(client->is_udp && client->typ == 'c') && (rs < n) && ((n - 32) > 0)) { //len = cs_recv(client->udp_fd, buf+32, n-32, 0); // read the rest of the packet int32_t tot=n-32, readed=0; len = 0; do{ readed = cs_recv(client->udp_fd, buf+32+len, tot, 0); // read the rest of the packet if (readed < 0){ if(errno == EINTR) { continue; } // try again in case of interrupt if(errno == EAGAIN) { continue; } //EAGAIN needs select procedure again cs_log_dbg(client->typ == 'c' ? D_CLIENT : D_READER, "ERROR: %s (errno=%d %s)", __func__, errno, strerror(errno)); break; } if (readed == 0){ // nothing to read left! break; } if (readed > 0){ // received something, add it! tot-=readed; len+=readed; } } while(tot!=0); if(len > 0) { rs += len; aes_decrypt(client->aes_keys, buf + 32, len); } if(len < 0) { rc = -1; goto out; } } cs_log_dump_dbg(client->typ == 'c' ? D_CLIENT : D_READER, buf, rs, "received %d bytes from %s", rs, remote_txt()); if(n < rs) cs_log_dbg(client->typ == 'c' ? D_CLIENT : D_READER, "ignoring %d bytes of garbage", rs - n); else if(n > rs) { rc = -3; } break; case 3: if(crc32(0L, buf + 20, buflen) != b2i(4, buf + 4)) { rc = -4; } if(!rc) { rc = n; } break; } } out: if((rs > 0) && ((rc == -1) || (rc == -2))) { cs_log_dump_dbg(client->typ == 'c' ? D_CLIENT : D_READER, buf, rs, "received %d bytes from %s (native)", rs, remote_txt()); } if(rc >= 0) { client->last = time(NULL); } // last client action is now switch(rc) { //case 0: break; case -1: cs_log("packet is too small (received %d bytes, expected %d bytes)", rs, l); break; case -2: if(cs_auth_client(client, 0, "unknown user")) { cs_disconnect_client(client); } break; case -3: cs_log("incomplete request !"); break; case -4: cs_log("checksum error (wrong password ?)"); break; case -5: cs_log_dbg(client->typ == 'c' ? D_CLIENT : D_READER, "connection closed"); break; //default: cs_log_dbg(D_TRACE, "camd35_recv returns rc=%d", rc); break; } return (rc); }
void *work_thread(void *ptr) { struct job_data *data = (struct job_data *)ptr; struct s_client *cl = data->cl; struct s_reader *reader = cl->reader; struct timeb start, end; // start time poll, end time poll struct job_data tmp_data; struct pollfd pfd[1]; SAFE_SETSPECIFIC(getclient, cl); cl->thread = pthread_self(); cl->thread_active = 1; set_work_thread_name(data); struct s_module *module = get_module(cl); uint16_t bufsize = module->bufsize; //CCCam needs more than 1024bytes! if(!bufsize) { bufsize = DEFAULT_MODULE_BUFSIZE; } uint8_t *mbuf; if(!cs_malloc(&mbuf, bufsize)) { return NULL; } cl->work_mbuf = mbuf; // Track locally allocated data, because some callback may call cs_exit/cs_disconect_client/pthread_exit and then mbuf would be leaked int32_t n = 0, rc = 0, i, idx, s; uint8_t dcw[16]; int8_t restart_reader = 0; while(cl->thread_active) { cs_ftime(&start); // register start time while(cl->thread_active) { if(!cl || cl->kill || !is_valid_client(cl)) { SAFE_MUTEX_LOCK(&cl->thread_lock); cl->thread_active = 0; SAFE_MUTEX_UNLOCK(&cl->thread_lock); cs_log_dbg(D_TRACE, "ending thread (kill)"); __free_job_data(cl, data); cl->work_mbuf = NULL; // Prevent free_client from freeing mbuf (->work_mbuf) free_client(cl); if(restart_reader) { restart_cardreader(reader, 0); } NULLFREE(mbuf); pthread_exit(NULL); return NULL; } if(data && data->action != ACTION_READER_CHECK_HEALTH) { cs_log_dbg(D_TRACE, "data from add_job action=%d client %c %s", data->action, cl->typ, username(cl)); } if(!data) { if(!cl->kill && cl->typ != 'r') { client_check_status(cl); } // do not call for physical readers as this might cause an endless job loop SAFE_MUTEX_LOCK(&cl->thread_lock); if(cl->joblist && ll_count(cl->joblist) > 0) { LL_ITER itr = ll_iter_create(cl->joblist); data = ll_iter_next_remove(&itr); if(data) { set_work_thread_name(data); } //cs_log_dbg(D_TRACE, "start next job from list action=%d", data->action); } SAFE_MUTEX_UNLOCK(&cl->thread_lock); } if(!data) { /* for serial client cl->pfd is file descriptor for serial port not socket for example: pfd=open("/dev/ttyUSB0"); */ if(!cl->pfd || module->listenertype == LIS_SERIAL) { break; } pfd[0].fd = cl->pfd; pfd[0].events = POLLIN | POLLPRI; SAFE_MUTEX_LOCK(&cl->thread_lock); cl->thread_active = 2; SAFE_MUTEX_UNLOCK(&cl->thread_lock); rc = poll(pfd, 1, 3000); SAFE_MUTEX_LOCK(&cl->thread_lock); cl->thread_active = 1; SAFE_MUTEX_UNLOCK(&cl->thread_lock); if(rc > 0) { cs_ftime(&end); // register end time cs_log_dbg(D_TRACE, "[OSCAM-WORK] new event %d occurred on fd %d after %"PRId64" ms inactivity", pfd[0].revents, pfd[0].fd, comp_timeb(&end, &start)); data = &tmp_data; data->ptr = NULL; cs_ftime(&start); // register start time for new poll next run if(reader) { data->action = ACTION_READER_REMOTE; } else { if(cl->is_udp) { data->action = ACTION_CLIENT_UDP; data->ptr = mbuf; data->len = bufsize; } else { data->action = ACTION_CLIENT_TCP; } if(pfd[0].revents & (POLLHUP | POLLNVAL | POLLERR)) { cl->kill = 1; } } } } if(!data) { continue; } if(!reader && data->action < ACTION_CLIENT_FIRST) { __free_job_data(cl, data); break; } if(!data->action) { break; } struct timeb actualtime; cs_ftime(&actualtime); int64_t gone = comp_timeb(&actualtime, &data->time); if(data != &tmp_data && gone > (int) cfg.ctimeout+1000) { cs_log_dbg(D_TRACE, "dropping client data for %s time %"PRId64" ms", username(cl), gone); __free_job_data(cl, data); continue; } if(data != &tmp_data) { cl->work_job_data = data; } // Track the current job_data switch(data->action) { case ACTION_READER_IDLE: reader_do_idle(reader); break; case ACTION_READER_REMOTE: s = check_fd_for_data(cl->pfd); if(s == 0) // no data, another thread already read from fd? { break; } if(s < 0) { if(reader->ph.type == MOD_CONN_TCP) { network_tcp_connection_close(reader, "disconnect"); } break; } rc = reader->ph.recv(cl, mbuf, bufsize); if(rc < 0) { if(reader->ph.type == MOD_CONN_TCP) { network_tcp_connection_close(reader, "disconnect on receive"); } break; } cl->last = time(NULL); // *********************************** TO BE REPLACE BY CS_FTIME() LATER **************** idx = reader->ph.c_recv_chk(cl, dcw, &rc, mbuf, rc); if(idx < 0) { break; } // no dcw received if(!idx) { idx = cl->last_idx; } reader->last_g = time(NULL); // *********************************** TO BE REPLACE BY CS_FTIME() LATER **************** // for reconnect timeout for(i = 0, n = 0; i < cfg.max_pending && n == 0; i++) { if(cl->ecmtask[i].idx == idx) { cl->pending--; casc_check_dcw(reader, i, rc, dcw); n++; } } break; case ACTION_READER_RESET: cardreader_do_reset(reader); break; case ACTION_READER_ECM_REQUEST: reader_get_ecm(reader, data->ptr); break; case ACTION_READER_EMM: reader_do_emm(reader, data->ptr); break; case ACTION_READER_CARDINFO: reader_do_card_info(reader); break; case ACTION_READER_POLL_STATUS: cardreader_poll_status(reader); break; case ACTION_READER_INIT: if(!cl->init_done) { reader_init(reader); } break; case ACTION_READER_RESTART: cl->kill = 1; restart_reader = 1; break; case ACTION_READER_RESET_FAST: reader->card_status = CARD_NEED_INIT; cardreader_do_reset(reader); break; case ACTION_READER_CHECK_HEALTH: cardreader_do_checkhealth(reader); break; case ACTION_READER_CAPMT_NOTIFY: if(reader->ph.c_capmt) { reader->ph.c_capmt(cl, data->ptr); } break; case ACTION_CLIENT_UDP: n = module->recv(cl, data->ptr, data->len); if(n < 0) { break; } module->s_handler(cl, data->ptr, n); break; case ACTION_CLIENT_TCP: s = check_fd_for_data(cl->pfd); if(s == 0) // no data, another thread already read from fd? { break; } if(s < 0) // system error or fd wants to be closed { cl->kill = 1; // kill client on next run continue; } n = module->recv(cl, mbuf, bufsize); if(n < 0) { cl->kill = 1; // kill client on next run continue; } module->s_handler(cl, mbuf, n); break; case ACTION_CACHEEX1_DELAY: cacheex_mode1_delay(data->ptr); break; case ACTION_CACHEEX_TIMEOUT: cacheex_timeout(data->ptr); break; case ACTION_FALLBACK_TIMEOUT: fallback_timeout(data->ptr); break; case ACTION_CLIENT_TIMEOUT: ecm_timeout(data->ptr); break; case ACTION_ECM_ANSWER_READER: chk_dcw(data->ptr); break; case ACTION_ECM_ANSWER_CACHE: write_ecm_answer_fromcache(data->ptr); break; case ACTION_CLIENT_INIT: if(module->s_init) { module->s_init(cl); } cl->is_udp = module->type == MOD_CONN_UDP; cl->init_done = 1; break; case ACTION_CLIENT_IDLE: if(module->s_idle) { module->s_idle(cl); } else { cs_log("user %s reached %d sec idle limit.", username(cl), cfg.cmaxidle); cl->kill = 1; } break; case ACTION_CACHE_PUSH_OUT: { cacheex_push_out(cl, data->ptr); break; } case ACTION_CLIENT_KILL: cl->kill = 1; break; case ACTION_CLIENT_SEND_MSG: { if (config_enabled(MODULE_CCCAM)) { struct s_clientmsg *clientmsg = (struct s_clientmsg *)data->ptr; cc_cmd_send(cl, clientmsg->msg, clientmsg->len, clientmsg->cmd); } break; } } // switch __free_job_data(cl, data); } if(thread_pipe[1] && (mbuf[0] != 0x00)) { cs_log_dump_dbg(D_TRACE, mbuf, 1, "[OSCAM-WORK] Write to pipe:"); if(write(thread_pipe[1], mbuf, 1) == -1) // wakeup client check { cs_log_dbg(D_TRACE, "[OSCAM-WORK] Writing to pipe failed (errno=%d %s)", errno, strerror(errno)); } } // Check for some race condition where while we ended, another thread added a job SAFE_MUTEX_LOCK(&cl->thread_lock); if(cl->joblist && ll_count(cl->joblist) > 0) { SAFE_MUTEX_UNLOCK(&cl->thread_lock); continue; } else { cl->thread_active = 0; SAFE_MUTEX_UNLOCK(&cl->thread_lock); break; } } cl->thread_active = 0; cl->work_mbuf = NULL; // Prevent free_client from freeing mbuf (->work_mbuf) NULLFREE(mbuf); pthread_exit(NULL); return NULL; }
static int32_t radegast_send_ecm(struct s_client *client, ECM_REQUEST *er) { uchar provid_buf[8]; uchar header[22] = "\x02\x01\x00\x06\x08\x30\x30\x30\x30\x30\x30\x30\x30\x07\x04\x30\x30\x30\x38\x08\x01\x02"; uchar *ecmbuf; uint8_t *SubECMp; uint8_t *via_ecm_mod; uint32_t n, k, Len, pos = 0; if(!radegast_connect()) { return (-1); } if(!cs_malloc(&ecmbuf, er->ecmlen + 30)) { return -1; } // Quickfix to suppress SubECMs with CWsSwap set to 01 // Applied only on Viaccess (CAID: 0x0500) // this reduce the size of the ECM from long to short // 40 07 03 0B 00 08 07 01 00 ... -> to keep // 40 07 03 0B 00 08 07 01 01 ... -> to delete // Thanks to luffy for the tip and the code. if(er->caid == 0x500) { cs_log_dump_dbg(D_ATR, er->ecm, er->ecmlen, "%s: ecm dump BEFORE suppressing SubECMs with CWsSwap set to 01", __func__); Len = er->ecmlen; if(cs_malloc (&via_ecm_mod, Len+4)) { if( er->ecm[4]==0x80 ) { memcpy(via_ecm_mod, er->ecm, 4); via_ecm_mod[1] = 0x70; via_ecm_mod[2] = 0x01; pos = 0x04; k = 4; while(k<Len) { SubECMp = (uint8_t *)&er->ecm[k]; if( ((pos+SubECMp[1]+2)>0xE0)||(pos+SubECMp[1]+2)>Len ) { break; } if (SubECMp[2]==0xD2) { if( SubECMp[0x0E] == 0x00 ) { memcpy(via_ecm_mod+pos, SubECMp, SubECMp[1]+2); via_ecm_mod[2] += SubECMp[1]+2; pos += SubECMp[1]+2; } } else if ( (SubECMp[2]==0x90 || SubECMp[2]==0x40) && SubECMp[3]==0x07 ) { if( SubECMp[0x0A] == 0x00 ) { memcpy(via_ecm_mod+pos, SubECMp, SubECMp[1]+2); via_ecm_mod[2] += SubECMp[1]+2; pos += SubECMp[1]+2; } } k += SubECMp[1] + 2; } Len = via_ecm_mod[2]+3; er->ecmlen = Len; memcpy(er->ecm, via_ecm_mod, Len); cs_log_dump_dbg(D_ATR, er->ecm, er->ecmlen, "%s: ecm dump AFTER suppressing SubECMs with CWsSwap set to 01", __func__); } NULLFREE(via_ecm_mod); } } ecmbuf[0] = 1; ecmbuf[1] = (er->ecmlen + 30 - 2) & 0xff; memcpy(ecmbuf + 2, header, sizeof(header)); for(n = 0; n < 4; n++) { snprintf((char *)provid_buf + (n * 2), sizeof(provid_buf) - (n * 2), "%02X", ((uchar *)(&er->prid))[4 - 1 - n]); } ecmbuf[7] = provid_buf[0]; ecmbuf[8] = provid_buf[1]; ecmbuf[9] = provid_buf[2]; ecmbuf[10] = provid_buf[3]; ecmbuf[11] = provid_buf[4]; ecmbuf[12] = provid_buf[5]; ecmbuf[13] = provid_buf[6]; ecmbuf[14] = provid_buf[7]; ecmbuf[2 + sizeof(header)] = 0xa; ecmbuf[3 + sizeof(header)] = 2; ecmbuf[4 + sizeof(header)] = er->caid >> 8; ecmbuf[5 + sizeof(header)] = er->caid & 0xff; ecmbuf[6 + sizeof(header)] = 3; ecmbuf[7 + sizeof(header)] = er->ecmlen & 0xff; memcpy(ecmbuf + 8 + sizeof(header), er->ecm, er->ecmlen); ecmbuf[4] = er->caid >> 8; client->reader->msg_idx = er->idx; n = send(client->pfd, ecmbuf, er->ecmlen + 30, 0); cs_log_dbg(D_TRACE, "radegast: sending ecm"); cs_log_dump_dbg(D_CLIENT, ecmbuf, er->ecmlen + 30, "ecm:"); NULLFREE(ecmbuf); return ((n < 1) ? (-1) : 0); }
static int32_t cacheex_add_to_cache_int(struct s_client *cl, ECM_REQUEST *er, int8_t csp) { if(er->rc >= E_NOTFOUND) { return 0; } if(!cl) { return 0; } if(!csp && cl->reader && cl->reader->cacheex.mode != 2) //from reader { cs_log_dbg(D_CACHEEX, "CACHEX received, but disabled for %s", username(cl)); return 0; } if(!csp && !cl->reader && cl->account && cl->account->cacheex.mode != 3) //from user { cs_log_dbg(D_CACHEEX, "CACHEX received, but disabled for %s", username(cl)); return 0; } if(!csp && !cl->reader && !cl->account) //not active! { cs_log_dbg(D_CACHEEX, "CACHEX received, but invalid client state %s", username(cl)); return 0; } uint8_t i, c; uint8_t null = 0; for(i = 0; i < 16; i += 4) { c = ((er->cw[i] + er->cw[i + 1] + er->cw[i + 2]) & 0xff); null |= (er->cw[i] | er->cw[i + 1] | er->cw[i + 2]); if(er->cw[i + 3] != c) { cs_log_dump_dbg(D_CACHEEX, er->cw, 16, "push received cw with chksum error from %s", csp ? "csp" : username(cl)); cl->cwcacheexerr++; if(cl->account) { cl->account->cwcacheexerr++; } return 0; } } if(null == 0 || chk_is_null_CW(er->cw)) { cs_log_dump_dbg(D_CACHEEX, er->cw, 16, "push received null cw from %s", csp ? "csp" : username(cl)); cl->cwcacheexerr++; if(cl->account) { cl->account->cwcacheexerr++; } return 0; } if(get_odd_even(er)==0){ cs_log_dbg(D_CACHEEX, "push received ecm with null odd/even byte from %s", csp ? "csp" : username(cl)); cl->cwcacheexerr++; if(cl->account) { cl->account->cwcacheexerr++; } return 0; } if(!chk_halfCW(er, er->cw)){ log_cacheex_cw(er, "bad half cw"); cl->cwcacheexerr++; if(cl->account) { cl->account->cwcacheexerr++; } return 0; } if((csp && cfg.csp.block_fakecws) || (cl->reader && cl->reader->cacheex.block_fakecws) || (!cl->reader && cl->account && cl->account->cacheex.block_fakecws)) { if(chk_is_fakecw(er->cw)) { cs_log_dbg(D_CACHEEX, "push received fake cw from %s", csp ? "csp" : username(cl)); cl->cwcacheexerr++; if(cl->account) { cl->account->cwcacheexerr++; } return 0; } } er->grp |= cl->grp; //ok for mode2 reader too: cl->reader->grp er->rc = E_CACHEEX; er->cacheex_src = cl; er->selected_reader = cl->reader; er->client = NULL; //No Owner! So no fallback! if(check_client(cl)) { cl->cwcacheexgot++; if(cl->account) { cl->account->cwcacheexgot++; } first_client->cwcacheexgot++; } cacheex_add_hitcache(cl, er); //we have to call it before add_cache, because in chk_process we could remove it! add_cache(er); cacheex_add_stats(cl, er->caid, er->srvid, er->prid, 1); cs_writelock(__func__, &ecm_pushed_deleted_lock); er->next = ecm_pushed_deleted; ecm_pushed_deleted = er; cs_writeunlock(__func__, &ecm_pushed_deleted_lock); return 1; //NO free, we have to wait cache push out stuff ends. }
static void process_clients(void) { int32_t i, k, j, rc, pfdcount = 0; struct s_client *cl; struct s_reader *rdr; struct pollfd *pfd; struct s_client **cl_list; struct timeb start, end; // start time poll, end time poll uint32_t cl_size = 0; uchar buf[10]; if(pipe(thread_pipe) == -1) { printf("cannot create pipe, errno=%d\n", errno); exit(1); } cl_size = chk_resize_cllist(&pfd, &cl_list, 0, 100); pfd[pfdcount].fd = thread_pipe[0]; pfd[pfdcount].events = POLLIN | POLLPRI; cl_list[pfdcount] = NULL; while(!exit_oscam) { pfdcount = 1; //connected tcp clients for(cl = first_client->next; cl; cl = cl->next) { if(cl->init_done && !cl->kill && cl->pfd && cl->typ == 'c' && !cl->is_udp) { if(cl->pfd && !cl->thread_active) { cl_size = chk_resize_cllist(&pfd, &cl_list, cl_size, pfdcount); cl_list[pfdcount] = cl; pfd[pfdcount].fd = cl->pfd; pfd[pfdcount++].events = POLLIN | POLLPRI; } } //reader: //TCP: // - TCP socket must be connected // - no active init thread //UDP: // - connection status ignored // - no active init thread rdr = cl->reader; if(rdr && cl->typ == 'p' && cl->init_done) { if(cl->pfd && !cl->thread_active && ((rdr->tcp_connected && rdr->ph.type == MOD_CONN_TCP) || (rdr->ph.type == MOD_CONN_UDP))) { cl_size = chk_resize_cllist(&pfd, &cl_list, cl_size, pfdcount); cl_list[pfdcount] = cl; pfd[pfdcount].fd = cl->pfd; pfd[pfdcount++].events = (POLLIN | POLLPRI); } } } //server (new tcp connections or udp messages) for(k = 0; k < CS_MAX_MOD; k++) { struct s_module *module = &modules[k]; if((module->type & MOD_CONN_NET)) { for(j = 0; j < module->ptab.nports; j++) { if(module->ptab.ports[j].fd) { cl_size = chk_resize_cllist(&pfd, &cl_list, cl_size, pfdcount); cl_list[pfdcount] = NULL; pfd[pfdcount].fd = module->ptab.ports[j].fd; pfd[pfdcount++].events = (POLLIN | POLLPRI); } } } } if(pfdcount >= 1024) { cs_log("WARNING: too many users!"); } cs_ftime(&start); // register start time rc = poll(pfd, pfdcount, 5000); if(rc < 1) { continue; } cs_ftime(&end); // register end time for(i = 0; i < pfdcount && rc > 0; i++) { if(pfd[i].revents == 0) { continue; } // skip sockets with no changes rc--; //event handled! cs_log_dbg(D_TRACE, "[OSCAM] new event %d occurred on fd %d after %"PRId64" ms inactivity", pfd[i].revents, pfd[i].fd, comp_timeb(&end, &start)); //clients cl = cl_list[i]; if(cl && !is_valid_client(cl)) { continue; } if(pfd[i].fd == thread_pipe[0] && (pfd[i].revents & (POLLIN | POLLPRI))) { // a thread ended and cl->pfd should be added to pollfd list again (thread_active==0) int32_t len = read(thread_pipe[0], buf, sizeof(buf)); if(len == -1) { cs_log_dbg(D_TRACE, "[OSCAM] Reading from pipe failed (errno=%d %s)", errno, strerror(errno)); } cs_log_dump_dbg(D_TRACE, buf, len, "[OSCAM] Readed:"); continue; } //clients // message on an open tcp connection if(cl && cl->init_done && cl->pfd && (cl->typ == 'c' || cl->typ == 'm')) { if(pfd[i].fd == cl->pfd && (pfd[i].revents & (POLLHUP | POLLNVAL | POLLERR))) { //client disconnects kill_thread(cl); continue; } if(pfd[i].fd == cl->pfd && (pfd[i].revents & (POLLIN | POLLPRI))) { add_job(cl, ACTION_CLIENT_TCP, NULL, 0); } } //reader // either an ecm answer, a keepalive or connection closed from a proxy // physical reader ('r') should never send data without request rdr = NULL; struct s_client *cl2 = NULL; if(cl && cl->typ == 'p') { rdr = cl->reader; if(rdr) { cl2 = rdr->client; } } if(rdr && cl2 && cl2->init_done) { if(cl2->pfd && pfd[i].fd == cl2->pfd && (pfd[i].revents & (POLLHUP | POLLNVAL | POLLERR))) { //connection to remote proxy was closed //oscam should check for rdr->tcp_connected and reconnect on next ecm request sent to the proxy network_tcp_connection_close(rdr, "closed"); rdr_log_dbg(rdr, D_READER, "connection closed"); } if(cl2->pfd && pfd[i].fd == cl2->pfd && (pfd[i].revents & (POLLIN | POLLPRI))) { add_job(cl2, ACTION_READER_REMOTE, NULL, 0); } } //server sockets // new connection on a tcp listen socket or new message on udp listen socket if(!cl && (pfd[i].revents & (POLLIN | POLLPRI))) { for(k = 0; k < CS_MAX_MOD; k++) { struct s_module *module = &modules[k]; if((module->type & MOD_CONN_NET)) { for(j = 0; j < module->ptab.nports; j++) { if(module->ptab.ports[j].fd && module->ptab.ports[j].fd == pfd[i].fd) { accept_connection(module, k, j); } } } } } } cs_ftime(&start); // register start time for new poll next run first_client->last = time((time_t *)0); } NULLFREE(pfd); NULLFREE(cl_list); return; }
static int32_t cacheex_add_to_cache_int(struct s_client *cl, ECM_REQUEST *er, int8_t csp) { if(er->rc >= E_NOTFOUND) { return 0; } if(!cl) { return 0; } if(!csp && cl->reader && cl->reader->cacheex.mode != 2) //from reader { cs_log_dbg(D_CACHEEX, "CACHEX received, but disabled for %s", username(cl)); return 0; } if(!csp && !cl->reader && cl->account && cl->account->cacheex.mode != 3) //from user { cs_log_dbg(D_CACHEEX, "CACHEX received, but disabled for %s", username(cl)); return 0; } if(!csp && !cl->reader && !cl->account) //not active! { cs_log_dbg(D_CACHEEX, "CACHEX received, but invalid client state %s", username(cl)); return 0; } if(!cfg.disablecrccws && ((cl->typ == 'c' && !cl->account->disablecrccacheex) || ( cl->typ == 'p' && !cl->reader->disablecrccws))) { uint8_t selectedForIgnChecksum = chk_if_ignore_checksum(er, &cfg.disablecrccws_only_for); if(cl->typ == 'c') { selectedForIgnChecksum += chk_if_ignore_checksum(er, &cl->account->disablecrccacheex_only_for); } if(cl->typ == 'p') { selectedForIgnChecksum += chk_if_ignore_checksum(er, &cl->reader->disablecrccws_only_for); } if(!selectedForIgnChecksum) { uint8_t i, c; for(i = 0; i < 16; i += 4) { c = ((er->cw[i] + er->cw[i + 1] + er->cw[i + 2]) & 0xff); if(er->cw[i + 3] != c) { cs_log_dump_dbg(D_CACHEEX, er->cw, 16, "push received cw with chksum error from %s", csp ? "csp" : username(cl)); cl->cwcacheexerr++; if(cl->account) { cl->account->cwcacheexerr++; } return 0; } } } } // Skip check for BISS1 - cw could be indeed zero // Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero if(chk_is_null_CW(er->cw) && !caid_is_biss(er->caid)) { cs_log_dump_dbg(D_CACHEEX, er->cw, 16, "push received null cw from %s", csp ? "csp" : username(cl)); cl->cwcacheexerr++; if(cl->account) { cl->account->cwcacheexerr++; } return 0; } // Don't check for BISS1 and BISS2 mode 1/E or fake caid (ECM is fake for them) // Don't check for BISS2 mode CA (ECM table is always 0x80) if(!caid_is_biss(er->caid) && !caid_is_fake(er->caid) && get_odd_even(er) == 0) { cs_log_dbg(D_CACHEEX, "push received ecm with null odd/even byte from %s", csp ? "csp" : username(cl)); cl->cwcacheexerr++; if(cl->account) { cl->account->cwcacheexerr++; } return 0; } if(!chk_halfCW(er, er->cw)) { log_cacheex_cw(er, "bad half cw"); cl->cwcacheexerr++; if(cl->account) { cl->account->cwcacheexerr++; } return 0; } if((csp && cfg.csp.block_fakecws) || (cl->reader && cl->reader->cacheex.block_fakecws) || (!cl->reader && cl->account && cl->account->cacheex.block_fakecws)) { if(chk_is_fakecw(er->cw)) { cs_log_dbg(D_CACHEEX, "push received fake cw from %s", csp ? "csp" : username(cl)); cl->cwcacheexerr++; if(cl->account) { cl->account->cwcacheexerr++; } return 0; } } er->grp |= cl->grp; // ok for mode2 reader too: cl->reader->grp er->rc = E_CACHEEX; er->cacheex_src = cl; er->selected_reader = cl->reader; er->client = NULL; // No Owner! So no fallback! if(check_client(cl)) { cl->cwcacheexgot++; if(cl->account) { cl->account->cwcacheexgot++; } first_client->cwcacheexgot++; } cacheex_add_hitcache(cl, er); // we have to call it before add_cache, because in chk_process we could remove it! add_cache(er); cacheex_add_stats(cl, er->caid, er->srvid, er->prid, 1); cs_writelock(__func__, &ecm_pushed_deleted_lock); er->next = ecm_pushed_deleted; ecm_pushed_deleted = er; cs_writeunlock(__func__, &ecm_pushed_deleted_lock); return 1; // NO free, we have to wait cache push out stuff ends. }