/* * (c) BayCom GmbH, http://www.baycom.de, info@baycom.de * * See the COPYING file for copyright information and * how to reach the author. * */ //#define TESTING #ifdef TESTING #include #include #include #include #include #include "ciparser.h" static unsigned char ll[] = { 0x00, 0x01, 0xA0, 0x28, 0x01, 0x90, 0x02, 0x00, 0x05, 0x9F, 0x80, 0x32, 0x1F, 0x03, 0x32, 0xC9, 0x01, 0x00, 0x0F, 0x01, 0x09, 0x06, 0x17, 0x62, 0xE0, 0x65, 0x00, 0x09, 0x09, 0x04, 0x17, 0x02, 0xE1, 0x2D, 0x02, 0x00, 0xA0, 0x00, 0x00, 0x04, 0x00, 0xA1, 0x00, 0x00 }; static unsigned char lr[] = { 0x00, 0x01, 0x80, 0x02, 0x01, 0x80 }; static unsigned char la[] = { 0x00, 0x01, 0xA0, 0x07, 0x01, 0x91, 0x04, 0x00, 0x01, 0x00, 0x41, 0x80, 0x02, 0x01, 0x00 }; static unsigned char lb[] = { 0x00, 0x01, 0xA0, 0x82, 0x00, 0x17, 0x01, 0x90, 0x02, 0x00, 0x03, 0x9F, 0x80, 0x31, 0x0E, 0x06, 0x02, 0x06, 0x02, 0x17, 0x02, 0x17, 0x62, 0x01, 0x00, 0x05, 0x00, 0x18, 0x00, 0x80, 0x02, 0x01, 0x00 }; static unsigned char lc[] = { 0x01, 0x02, 0xA0, 0x5F, 0x02, 0x90, 0x02, 0x00, 0x06, 0x9F, 0x80, 0x32, 0x56, 0x03, 0x03, 0x8B, 0x01, 0x00, 0x00, 0x02, 0x00, 0xA3, 0x00, 0x23, 0x01, 0x09, 0x0F, 0x05, 0x00, 0xE2, 0xC3, 0x10, 0x01, 0x00, 0x13, 0x01, 0x20, 0x14, 0x03, 0x00, 0x94, 0x0D, 0x09, 0x0F, 0x05, 0x00, 0xE2, 0xCD, 0x10, 0x01, 0x00, 0x13, 0x01, 0x20, 0x14, 0x03, 0x02, 0x38, 0x08, 0x04, 0x00, 0x5C, 0x00, 0x23, 0x01, 0x09, 0x0F, 0x05, 0x00, 0xE2, 0xC3, 0x10, 0x01, 0x00, 0x13, 0x01, 0x20, 0x14, 0x03, 0x00, 0x94, 0x0D, 0x09, 0x0F, 0x05, 0x00, 0xE2, 0xCD, 0x10, 0x01, 0x00, 0x13, 0x01, 0x20, 0x14, 0x03, 0x02, 0x38, 0x08 }; static unsigned char ld[] = { 0x00, 0x01, 0xA0, 0x82, 0x00, 0x10, 0x01, 0x90, 0x02, 0x00, 0x03, 0x9F, 0x80, 0x33, 0x07, 0x2D, 0xB9, 0x01, 0x81, 0x00, 0x08, 0x00, 0x80, 0x02, 0x01, 0x00 }; static unsigned char le[] = { 0x00, 0x01, 0xA0, 0x34, 0x01, 0x90, 0x02, 0x00, 0x03, 0x9F, 0x80, 0x32, 0x2B, 0x03, 0x00, 0x0B, 0x01, 0x00, 0x11, 0x01, 0x09, 0x06, 0x17, 0x22, 0xF0, 0x0B, 0x00, 0x0B, 0x09, 0x06, 0x17, 0x02, 0xF0, 0x0B, 0x00, 0x0B, 0x02, 0x06, 0xFF, 0x00, 0x00, 0x04, 0x07, 0x00, 0x00, 0x00, 0x04, 0x07, 0x01, 0x00, 0x00, 0x00, 0x07, 0x03, 0x00, 0x00}; #define dbg(format, arg...) printf("%s:%d " format , __FILE__ , __LINE__ , ## arg) #define err(format, arg...) {printf("err:%s:%d: %s (%d): " format , __FILE__ , __LINE__ ,strerror(errno), errno, ## arg);print_trace();abort();} #define info(format, arg...) printf("%s:%d: " format , __FILE__ , __LINE__ ,## arg) #define warn(format, arg...) printf("%s:%d: " format , __FILE__ , __LINE__ ,## arg) #define STATIC #else //#define DEBUG #include "headers.h" #endif #define CA_MAX_CAIDS 16 #define CA_MAX_PIDS 16 #ifndef CA_MAX_SLOTS #define CA_MAX_SLOTS 3 #endif typedef struct { u_int16_t caid[CA_MAX_CAIDS]; u_int16_t pid[CA_MAX_PIDS]; u_int16_t capid[CA_MAX_PIDS]; } caid_pid_list_t; static caid_pid_list_t cpl[CA_MAX_SLOTS]; STATIC void dump(u_int8_t *data, int len) { #ifdef DEBUG int j; printf("Dump: "); for(j=0;j= CA_MAX_SLOTS) { return -1; } int i; for (i = 0; i < CA_MAX_PIDS; i++) { if (pid == cpl[slot].pid[i]) return 1; } return 0; } STATIC int ci_cpl_find_caid (int slot, int caid) { if (slot < 0 || slot >= CA_MAX_SLOTS) { return -1; } int i; for (i = 0; i < CA_MAX_CAIDS; i++) { if (caid == cpl[slot].caid[i]) return 1; } return 0; } STATIC int ci_cpl_find_capid (int slot, int pid) { if (slot < 0 || slot >= CA_MAX_SLOTS) { return -1; } int i; for (i = 0; i < CA_MAX_PIDS; i++) { if (pid == cpl[slot].capid[i]) return 1; } return 0; } STATIC int ci_cpl_delete_pid (int slot, int pid) { if (slot < 0 || slot >= CA_MAX_SLOTS) { return -1; } int i; for (i = 0; i < CA_MAX_PIDS; i++) { if (cpl[slot].pid[i]==pid) { cpl[slot].pid[i] = 0; dbg ("-------> Slot: %d Deleted pid: %04x\n", slot, pid); return 1; } } return 0; } STATIC int ci_cpl_update_pid (int slot, int pid) { if (slot < 0 || slot >= CA_MAX_SLOTS) { return -1; } if (!ci_cpl_find_pid (slot, pid)) { int i; for (i = 0; i < CA_MAX_PIDS; i++) { if (!cpl[slot].pid[i]) { cpl[slot].pid[i] = pid; dbg ("-------> Slot: %d Added pid: %04x\n", slot, pid); return 1; } } } return 0; } STATIC int ci_cpl_update_caid (int slot, int caid) { if (slot < 0 || slot >= CA_MAX_SLOTS) { return -1; } if (!ci_cpl_find_caid (slot, caid)) { int i; for (i = 0; i < CA_MAX_CAIDS; i++) { if (!cpl[slot].caid[i]) { cpl[slot].caid[i] = caid; dbg ("-------> Slot: %d Added caid: %04x\n", slot, caid); return 1; } } } return 0; } STATIC int ci_cpl_update_capid (int slot, int capid) { if (slot < 0 || slot >= CA_MAX_SLOTS) { return -1; } if (!ci_cpl_find_capid (slot, capid)) { int i; for (i = 0; i < CA_MAX_PIDS; i++) { if (!cpl[slot].capid[i]) { cpl[slot].capid[i] = capid; dbg ("-------> Slot: %d Added capid: %04x\n", slot, capid); return 1; } } } return 0; } int ci_cpl_find_caid_by_pid (int pid) { int i; int slot; if(!pid) { return 0; } for (slot = 0; slot < CA_MAX_SLOTS; slot++) { for (i = 0; i < CA_MAX_PIDS; i++) { if (pid == cpl[slot].pid[i]) { return cpl[slot].caid[0]; } } } return 0; } int ci_cpl_find_slot_by_caid_and_pid (int caid, int pid) { int slot; for (slot = 0; slot < CA_MAX_SLOTS; slot++) { if (ci_cpl_find_pid (slot, pid) && ci_cpl_find_caid (slot, caid)) { return slot; } } return -1; } int ci_cpl_clear (int slot) { if (slot < 0 || slot >= CA_MAX_SLOTS) { return -1; } memset (&cpl[slot], 0, sizeof (caid_pid_list_t)); return 0; } int ci_cpl_clear_pids (int slot) { if (slot < 0 || slot >= CA_MAX_SLOTS) { return -1; } memset (cpl[slot].pid, 0, sizeof (u_int16_t) * CA_MAX_PIDS); return 0; } int ci_cpl_clear_caids (int slot) { if (slot < 0 || slot >= CA_MAX_SLOTS) { return -1; } memset (cpl[slot].caid, 0, sizeof (u_int16_t) * CA_MAX_CAIDS); return 0; } int ci_cpl_clear_capids (int slot) { if (slot < 0 || slot >= CA_MAX_SLOTS) { return -1; } memset (cpl[slot].capid, 0, sizeof (u_int16_t) * CA_MAX_PIDS); return 0; } STATIC int ci_decode_length (unsigned int *len, u_int8_t * v) { int ret = 0; if (*v & LENGTH_SIZE_INDICATOR) { int l = *v & 0x7f; if (l > 4) { return -1; } ret = l + 1; *len = 0; while (l--) { v++; *len <<= 8; *len |= *v; } } else { *len = *v; ret = 1; } return ret; } #if 0 STATIC int ci_decode_al_ca_info (ci_al_t * al) { int i = 0; u_int8_t *data = al->data; int len = al->length; if (len & 1) { dbg ("ci_decode_al_ca_info: invalid length %d\n", len); } len >>= 1; u_int16_t *caid = (u_int16_t *) malloc (sizeof (u_int16_t) * len); ci_cpl_clear_caids (al->sl->tl->ll->slot); while (i < len) { caid[i++] = ntohs16 (data); data += 2; ci_cpl_update_caid (al->sl->tl->ll->slot, caid[i - 1]); dbg ("CAID[%d]: %04x\n", i - 1, caid[i - 1]); } if (caid) { free (caid); } return data - al->data; } #endif STATIC int ca_decode_ca_descr (ca_desc_t ** cadescr, int count, u_int8_t * data, int len, int *magic) { *cadescr = (ca_desc_t *) realloc (*cadescr, sizeof (ca_desc_t *) * (count + 1)); if (!*cadescr) { err ("ca_decode_ca_descr: out of memory\n"); } ca_desc_t *c = *cadescr + count; // u_int8_t descriptor_tag = *data; data++; u_int8_t descriptor_length = *data; data++; c->ca_id = ntohs16 (data); data += 2; c->ca_pid = ntohs16 (data); data += 2; dbg ("cadescr: %p %d ca_id: %04x ca_pid: %04x\n", cadescr, count, c->ca_id, c->ca_pid); if(magic && c->ca_id > 0 && c->ca_id < 3 && c->ca_pid > 0 && c->ca_pid < 3 && c->ca_id == c->ca_pid){ *magic = c->ca_id; } return descriptor_length + 2; } STATIC int ci_decode_al_ca_pmt (ci_al_t * al) { ca_pmt_t p; int magic = 0; int slot = 0; int cleared = 0; memset (&p, 0, sizeof (ca_pmt_t)); int ret; u_int8_t *data = al->data; int len; p.ca_pmt_list_management = *data; data++; p.program_number = ntohs16 (data); data += 2; p.version_number = *data; data++; p.program_info_length = (u_int16_t) ntohs16 (data); data += 2; dbg ("ci_decode_al_ca_pmt: ca_pmt_list_management:%02x program_number:%04x version_number:%02x program_info_length:%04x\n", p.ca_pmt_list_management, p.program_number, p.version_number, p.program_info_length); if (p.program_info_length) { int ca_descr_count = 0; len = p.program_info_length - 1; p.ca_pmt_cmd_id = *data; dbg ("p.ca_pmt_cmd_id:%02x\n", p.ca_pmt_cmd_id); data++; while (len>0) { ret = ca_decode_ca_descr (&p.cadescr, ca_descr_count, data, len, &magic); if (magic) slot = magic - 1; else slot = al->sl->tl->ll->slot; if (!cleared) { if(p.ca_pmt_list_management == CPLM_ONLY || p.ca_pmt_list_management == CPLM_FIRST || p.ca_pmt_list_management == CPLM_UPDATE) { ci_cpl_clear_pids(slot); ci_cpl_clear_capids(slot); ci_cpl_clear_caids(slot); cleared = 1; } } if (ret < 0) { warn ("error decoding ca_descriptor\n"); break; } if((magic != p.cadescr[ca_descr_count].ca_id) || (magic != p.cadescr[ca_descr_count].ca_pid)){ ci_cpl_update_caid (slot, p.cadescr[ca_descr_count].ca_id); ci_cpl_update_capid (slot, p.cadescr[ca_descr_count].ca_pid); } ca_descr_count++; data += ret; len -= ret; } if (p.cadescr) { free (p.cadescr); } } len = al->length - (data - al->data); int pidn = 0; while (len>0) { p.pidinfo = (pidinfo_t *) realloc (p.pidinfo, sizeof (pidinfo_t) * (pidn + 1)); if (!p.pidinfo) { err ("ci_decode_al_ca_pmt: out of memory"); } memset (&p.pidinfo[pidn], 0, sizeof (pidinfo_t)); p.pidinfo[pidn].stream_type = *data; data++; len--; p.pidinfo[pidn].pid = ntohs16 (data); data += 2; len -= 2; p.pidinfo[pidn].es_info_length = ntohs16 (data); data += 2; len -= 2; dbg ("len: %d count: %d, stream_type: %02x, pid: %04x es_info_length: %04x\n", len, pidn, p.pidinfo[pidn].stream_type, p.pidinfo[pidn].pid, p.pidinfo[pidn].es_info_length); if (p.pidinfo[pidn].es_info_length) { int pi_len = p.pidinfo[pidn].es_info_length - 1; p.pidinfo[pidn].ca_pmt_cmd_id = *data; data++; len--; int pid_ca_descr_count = 0; while (pi_len>0) { ret = ca_decode_ca_descr (&p.pidinfo[pidn].cadescr, pid_ca_descr_count, data, pi_len, NULL); if (!cleared) { if(p.ca_pmt_list_management == CPLM_ONLY || p.ca_pmt_list_management == CPLM_FIRST || p.ca_pmt_list_management == CPLM_UPDATE) { ci_cpl_clear_pids(slot); ci_cpl_clear_capids(slot); ci_cpl_clear_caids(slot); cleared = 1; } } if (ret < 0) { warn ("error decoding ca_descriptor\n"); break; } if((magic != p.pidinfo[pidn].cadescr[pid_ca_descr_count].ca_id) || (magic != p.pidinfo[pidn].cadescr[pid_ca_descr_count].ca_pid)){ ci_cpl_update_pid (slot, p.pidinfo[pidn].pid); ci_cpl_update_caid (slot, p.pidinfo[pidn].cadescr[pid_ca_descr_count].ca_id); ci_cpl_update_capid (slot, p.pidinfo[pidn].cadescr[pid_ca_descr_count].ca_pid); } pid_ca_descr_count++; data += ret; pi_len -= ret; len -= ret; } } if (p.pidinfo[pidn].cadescr) { free (p.pidinfo[pidn].cadescr); } pidn++; } if (p.pidinfo) { free (p.pidinfo); } return 0; } STATIC int ci_decode_al_ca_pmt_reply (ci_al_t * al) { ca_pmt_reply_t p; memset (&p, 0, sizeof (ca_pmt_reply_t)); u_int8_t *data = al->data; int len; p.program_number = ntohs16 (data); data += 2; p.version_number = *data; data++; p.ca_enable = *data; data++; len = al->length - (data - al->data); int pidn = 0; dbg ("ci_decode_al_ca_pmt_reply: program_number: %04x ca_enable: %02x\n", p.program_number, p.ca_enable); while (len>0) { p.pidcaenable = (pid_ca_enable_t *) realloc (p.pidcaenable, sizeof (pid_ca_enable_t) * (pidn + 1)); if (!p.pidcaenable) { err ("ci_decode_al_ca_pmt_reply: out of memory\n"); } memset (&p.pidcaenable[pidn], 0, sizeof (pid_ca_enable_t)); p.pidcaenable[pidn].pid = ntohs16 (data); data += 2; p.pidcaenable[pidn].ca_enable = *data; data++; len -= 3; if ((p.pidcaenable[pidn].ca_enable == CPCI_OK_DESCRAMBLING) || (p.pidcaenable[pidn].ca_enable == CPCI_OK_MMI) || (p.pidcaenable[pidn].ca_enable == CPCI_QUERY)) { ci_cpl_update_pid (al->sl->tl->ll->slot, p.pidcaenable[pidn].pid); } else { ci_cpl_delete_pid (al->sl->tl->ll->slot, p.pidcaenable[pidn].pid); } dbg ("count: %d pid: %04x pid_ca_enable: %02x\n", pidn, p.pidcaenable[pidn].pid, p.pidcaenable[pidn].ca_enable); pidn++; } if (p.pidcaenable) { free (p.pidcaenable); } return 0; } STATIC int ci_decode_al (ci_sl_t * sl) { int ret = 0; int done = 0; int len = 3; ci_al_t al; u_int8_t *data = sl->data; al.sl = sl; al.tag = 0; while (len--) { al.tag <<= 8; al.tag |= *data; data++; } done += 3; ret = ci_decode_length (&al.length, data); if (ret < 0) { warn ("ci_decode_al ci_decode_length failed\n"); return ret; } data += ret; done += ret; //Fake al.data = data; dbg ("ci_decode_al: tag:%03x length: %02x data[0]:%02x done: %02x\n", al.tag, al.length, al.data[0], done); switch (al.tag) { case AOT_CA_INFO: // ci_decode_al_ca_info (&al); break; case AOT_CA_PMT: ci_decode_al_ca_pmt (&al); break; case AOT_CA_PMT_REPLY: ci_decode_al_ca_pmt_reply (&al); break; } done += al.length; dbg ("ci_decode_al: done %02x\n", done); return done; } STATIC int ci_decode_sl (ci_tl_t * tl) { int ret = 0; int done = 0; unsigned int len; ci_sl_t sl; u_int8_t *data = tl->data; sl.tl = tl; sl.tag = *data; data++; done++; if(sl.tag != ST_SESSION_NUMBER) { return tl->length; } ret = ci_decode_length (&len, data); if (ret < 0) { warn ("ci_decode_sl ci_decode_length failed\n"); return ret; } data += ret; done += ret; if (len > 4) { warn ("invalid length (%d) for session_object_value\n", len); return -1; } sl.object_value = 0; while (len--) { sl.object_value <<= 8; sl.object_value |= *data; data++; done++; } sl.data = data; sl.length = tl->length - done; while (sl.length>0) { dbg ("ci_decode_sl: object_value:%02x length: %02x done: %02x\n", sl.object_value, sl.length, done); ret = ci_decode_al (&sl); if (ret < 0) { warn ("ci_decode_al failed\n"); return ret; } sl.length -= ret; sl.data += ret; done += ret; } dbg ("ci_decode_sl: done %02x\n", done); return done; } STATIC int ci_decode_tl (ci_ll_t * ll) { int ret = 0; int done = 0; ci_tl_t tl; u_int8_t *data = ll->data; tl.ll = ll; tl.c_tpdu_tag = *data; data++; done++; ret = ci_decode_length (&tl.length, data); if (ret < 0) { warn ("ci_decode_tl ci_decode_length failed\n"); return ret; } data += ret; done += ret; tl.tcid = *data; data++; done++; if (tl.tcid != ll->tcid) { warn ("Error: redundant tcid mismatch %02x %02x\n",tl.tcid, ll->tcid); return -1; } tl.data = data; //According to A.4.1.1 tl.length--; while (tl.length>0) { dbg ("ci_decode_tl: c_tpdu_tag:%02x tcid:%02x length: %02x done: %02x\n", tl.c_tpdu_tag, tl.tcid, tl.length, done); if (tl.c_tpdu_tag == T_DATA_LAST || tl.c_tpdu_tag == T_DATA_MORE) { ret = ci_decode_sl (&tl); if (ret < 0) { warn ("ci_decode_sl failed\n"); return ret; } } else { ret = tl.length; } tl.length -= ret; tl.data += ret; done += ret; } dbg ("ci_decode_tl: done %02x\n", done); return done; } int ci_decode_ll (uint8_t * tpdu, int len) { int ret = 0; int done = 0; u_int8_t *data=tpdu; ci_ll_t ll; dump(tpdu,len); ll.slot = *data; data++; ll.tcid = *data; data++; ll.data = data; ll.length = len - (data-tpdu); while (ll.length) { dbg ("ci_decode_ll: slot:%02x tcid:%02x length: %02x\n", ll.slot, ll.tcid, ll.length); ret = ci_decode_tl (&ll); if (ret < 0) { warn ("ci_decode_tl failed\n"); return ret; } ll.length -= ret; ll.data += ret; } dbg ("ci_decode_ll: done %02x\n", len); return done; } #ifdef TESTING int main (int argc, char **argv) { int ret; printf ("ci_decode_ll len: %02x\n", sizeof (lb)); ret = ci_decode_ll (lb, sizeof (lb)); printf ("ci_decode_ll ret: %02x\n", ret); printf ("ci_decode_ll len: %02x\n", sizeof (ll)); ret = ci_decode_ll (ll, sizeof (ll)); printf ("ci_decode_ll ret: %02x\n", ret); printf ("ci_decode_ll len: %02x\n", sizeof (ld)); ret = ci_decode_ll (ld, sizeof (ld)); printf ("ci_decode_ll ret: %02x\n", ret); printf ("ci_decode_ll len: %02x\n", sizeof (lc)); ret = ci_decode_ll (lc, sizeof (lc)); printf ("ci_decode_ll ret: %02x\n", ret); printf ("ci_decode_ll len: %02x\n", sizeof (le)); ret = ci_decode_ll (le, sizeof (le)); printf ("ci_decode_ll ret: %02x\n", ret); // printf ("caid %04x for pid %04x\n", ci_cpl_find_caid_by_pid (0x5c), 0x5c); return 0; } #endif