static void sort_ecmrl(struct s_reader *reader) { int32_t i, j, loc; struct ecmrl tmp; for(i = 0; i < reader->ratelimitecm; i++) // inspect all slots { if(reader->rlecmh[i].last.time == -1) { continue; } // skip empty slots loc = i; tmp = reader->rlecmh[i]; // tmp is ecm in slot to evaluate for(j = i + 1; j < MAXECMRATELIMIT; j++) // inspect all slots above the slot to be inspected { if(reader->rlecmh[j].last.time == -1) { continue; } // skip empty slots int32_t gone = comp_timeb(&reader->rlecmh[i].last, &tmp.last); if(gone > 0) // is higher slot holding a younger ecmrequest? { loc = j; // found a younger one tmp = reader->rlecmh[j]; // copy the ecm in younger slot } } if(loc != i) // Did we find a younger ecmrequest? { reader->rlecmh[loc] = reader->rlecmh[i]; // place older request in slot of younger one we found reader->rlecmh[i] = tmp; // place younger request in slot of older request } } // release all slots above ratelimit ecm for(i = reader->ratelimitecm; i < MAXECMRATELIMIT; i++) { reader->rlecmh[i].last.time = -1; reader->rlecmh[i].srvid = -1; reader->rlecmh[i].kindecm = 0; reader->rlecmh[i].once = 0; } }
void cs_log_int(uint16_t mask, int8_t lock __attribute__((unused)), const uchar *buf, int32_t n, const char *fmt, ...) { if((mask & cs_dblevel) || !mask) { va_list params; int32_t dupl_header_len, repeated_line, i, len = 0; pthread_mutex_lock(&log_mutex); if(fmt) { va_start(params, fmt); len = get_log_header(1, log_txt); vsnprintf(log_txt + len, sizeof(log_txt) - len, fmt, params); va_end(params); if(cfg.logduplicatelines) { memcpy(last_log_txt, log_txt + len, LOG_BUF_SIZE); write_to_log_int(log_txt, len); } else { repeated_line = strcmp(last_log_txt, log_txt + len) == 0; if(last_log_duplicates > 0) { if(!cs_valid_time(&last_log_ts)) // Must be initialized once { last_log_ts = log_ts; } // Report duplicated lines when the new log line is different // than the old or 60 seconds have passed. int32_t gone = comp_timeb(&log_ts, &last_log_ts); if(!repeated_line || gone >= 60*1000) { dupl_header_len = get_log_header(2, dupl); snprintf(dupl + dupl_header_len - 1, sizeof(dupl) - dupl_header_len, "--- Skipped %u duplicated log lines ---", last_log_duplicates); write_to_log_int(dupl, 0); last_log_duplicates = 0; last_log_ts = log_ts; } } if(!repeated_line) { memcpy(last_log_txt, log_txt + len, LOG_BUF_SIZE); write_to_log_int(log_txt, len); } else { last_log_duplicates++; } } } if(buf) { for(i = 0; i < n; i += 16) { len = get_log_header(0, log_txt); cs_hexdump(1, buf + i, (n - i > 16) ? 16 : n - i, log_txt + len, sizeof(log_txt) - len); write_to_log_int(log_txt, len); } } pthread_mutex_unlock(&log_mutex); } }
static int32_t ecm_ratelimit_findspace(struct s_reader *reader, ECM_REQUEST *er, struct ecmrl rl, int32_t reader_mode) { int32_t h, foundspace = -1; int32_t maxecms = MAXECMRATELIMIT; // init maxecms int32_t totalecms = 0; // init totalecms struct timeb actualtime; cs_ftime(&actualtime); for(h = 0; h < MAXECMRATELIMIT; h++) // release slots with srvid that are overtime, even if not called from reader module to maximize available slots! { if(reader->rlecmh[h].last.time == -1) { continue; } int32_t gone = comp_timeb(&actualtime, &reader->rlecmh[h].last); if( gone >= (reader->rlecmh[h].ratelimittime + reader->rlecmh[h].srvidholdtime) || gone < 0) // gone <0 fixup for bad systemtime on dvb receivers while changing transponders { cs_debug_mask(D_CLIENT, "ratelimiter srvid %04X released from slot #%d/%d of reader %s (%d>=%d ratelimit ms + %d ms srvidhold!)", reader->rlecmh[h].srvid, h + 1, MAXECMRATELIMIT, reader->label, gone, reader->rlecmh[h].ratelimittime, reader->rlecmh[h].srvidholdtime); reader->rlecmh[h].last.time = -1; reader->rlecmh[h].srvid = -1; reader->rlecmh[h].kindecm = 0; } if(reader->rlecmh[h].last.time == -1) { continue; } if(reader->rlecmh[h].ratelimitecm < maxecms) { maxecms = reader->rlecmh[h].ratelimitecm; } // we found a more critical ratelimit srvid totalecms++; } cs_debug_mask(D_CLIENT, "ratelimiter found total of %d srvid for reader %s most critical is limited to %d requests", totalecms, reader->label, maxecms); if(reader->cooldown[0] && reader->cooldownstate != 1) { maxecms = MAXECMRATELIMIT; } // dont apply ratelimits if cooldown isnt in use or not in effect for(h = 0; h < MAXECMRATELIMIT; h++) // check if srvid is already in a slot { if(reader->rlecmh[h].last.time == -1) { continue; } if(reader->rlecmh[h].srvid == er->srvid && reader->rlecmh[h].caid == rl.caid && reader->rlecmh[h].provid == rl.provid && (!reader->rlecmh[h].chid || (reader->rlecmh[h].chid == rl.chid))) { int32_t gone = comp_timeb(&actualtime, &reader->rlecmh[h].last); cs_debug_mask(D_CLIENT, "ratelimiter found srvid %04X for %d ms in slot #%d/%d of reader %s", er->srvid, gone, h + 1, MAXECMRATELIMIT, reader->label); // check ecmunique if enabled and ecmunique time is done if(reader_mode && reader->ecmunique) { gone = comp_timeb(&actualtime, &reader->rlecmh[h].last); if(gone < reader->ratelimittime) { if(memcmp(reader->rlecmh[h].ecmd5, er->ecmd5, CS_ECMSTORESIZE)) { if(er->ecm[0] == reader->rlecmh[h].kindecm) { char ecmd5[17 * 3]; cs_hexdump(0, reader->rlecmh[h].ecmd5, 16, ecmd5, sizeof(ecmd5)); cs_debug_mask(D_CLIENT, "ratelimiter ecm %s in this slot for next %d ms!", ecmd5, (int)(reader->rlecmh[h].ratelimittime - gone)); struct ecm_request_t *erold = NULL; if(!cs_malloc(&erold, sizeof(struct ecm_request_t))) { return -2; } memcpy(erold, er, sizeof(struct ecm_request_t)); // copy ecm all memcpy(erold->ecmd5, reader->rlecmh[h].ecmd5, CS_ECMSTORESIZE); // replace md5 hash struct ecm_request_t *ecm = NULL; ecm = check_cache(erold, erold->client); //CHECK IF FOUND ECM IN CACHE NULLFREE(erold); if(ecm) //found in cache { write_ecm_answer(reader, er, ecm->rc, ecm->rcEx, ecm->cw, NULL); } else { write_ecm_answer(reader, er, E_NOTFOUND, E2_RATELIMIT, NULL, "Ratelimiter: no slots free!"); } NULLFREE(ecm); return -2; } continue; } } if((er->ecm[0] == reader->rlecmh[h].kindecm) && (gone <= (reader->ratelimittime + reader->srvidholdtime))) { cs_debug_mask(D_CLIENT, "ratelimiter srvid %04X ecm type %s, only allowing %s for next %d ms in slot #%d/%d of reader %s -> skipping this slot!", reader->rlecmh[h].srvid, (reader->rlecmh[h].kindecm == 0x80 ? "even" : "odd"), (reader->rlecmh[h].kindecm == 0x80 ? "odd" : "even"), (int)(reader->rlecmh[h].ratelimittime + reader->rlecmh[h].srvidholdtime - gone), h + 1, maxecms, reader->label); continue; } } if(h > 0) { for(foundspace = 0; foundspace < h; foundspace++) // check for free lower slot { if(reader->rlecmh[foundspace].last.time == -1) { reader->rlecmh[foundspace] = reader->rlecmh[h]; // replace ecm request info reader->rlecmh[h].srvid = -1; reader->rlecmh[h].last.time = -1; if(foundspace < maxecms) { cs_debug_mask(D_CLIENT, "ratelimiter moved srvid %04X to slot #%d/%d of reader %s", er->srvid, foundspace + 1, maxecms, reader->label); return foundspace; // moving to lower free slot! } else { cs_debug_mask(D_CLIENT, "ratelimiter removed srvid %04X from slot #%d/%d of reader %s", er->srvid, foundspace + 1, maxecms, reader->label); reader->rlecmh[foundspace].last.time = -1; // free this slot since we are over ratelimit! return -1; // sorry, ratelimit! } } } } if(h < maxecms) // found but cant move to lower position! { return h; // return position if within ratelimits! } else { reader->rlecmh[h].last.time = -1; // free this slot since we are over ratelimit! cs_debug_mask(D_CLIENT, "ratelimiter removed srvid %04X from slot #%d/%d of reader %s", er->srvid, h + 1, maxecms, reader->label); return -1; // sorry, ratelimit! } } } // srvid not found in slots! if((reader->cooldown[0] && reader->cooldownstate == 1) || !reader->cooldown[0]) { ; // do we use cooldown at all, are we in cooldown fase? // we are in cooldown or no cooldown configured! if(totalecms + 1 > maxecms || totalecms + 1 > rl.ratelimitecm) // check if this channel fits in! { cs_debug_mask(D_CLIENT, "ratelimiter for reader %s has no free slots!", reader->label); return -1; } } else { maxecms = MAXECMRATELIMIT; // no limits right now! } for(h = 0; h < maxecms; h++) // check for free slot { if(reader->rlecmh[h].last.time == -1) { if(reader_mode) { cs_debug_mask(D_CLIENT, "ratelimiter added srvid %04X to slot #%d/%d of reader %s", er->srvid, h + 1, maxecms, reader->label); } return h; // free slot found -> assign it! } else { int32_t gone = comp_timeb(&actualtime, &reader->rlecmh[h].last); cs_debug_mask(D_CLIENT, "ratelimiter srvid %04X for %d ms present in slot #%d/%d of reader %s", reader->rlecmh[h].srvid, gone , h + 1, maxecms, reader->label); } //occupied slots } #ifdef HAVE_DVBAPI /* Overide ratelimit priority for dvbapi request */ foundspace = -1; int32_t gone = 0; if((cfg.dvbapi_enabled == 1) && streq(er->client->account->usr, cfg.dvbapi_usr)) { if(reader->lastdvbapirateoverride.time == 0) { // fixup for first run! gone = comp_timeb(&actualtime, &reader->lastdvbapirateoverride); } if(gone > reader->ratelimittime) { struct timeb minecmtime = actualtime; for(h = 0; h < MAXECMRATELIMIT; h++) { gone = comp_timeb(&minecmtime, &reader->rlecmh[h].last); if(gone > 0) { minecmtime = reader->rlecmh[h].last; foundspace = h; } } reader->lastdvbapirateoverride = actualtime; cs_debug_mask(D_CLIENT, "prioritizing DVBAPI user %s over other watching client", er->client->account->usr); cs_debug_mask(D_CLIENT, "ratelimiter forcing srvid %04X into slot #%d/%d of reader %s", er->srvid, foundspace + 1, maxecms, reader->label); return foundspace; } else cs_debug_mask(D_CLIENT, "DVBAPI User %s is switching too fast for ratelimit and can't be prioritized!", er->client->account->usr); } #endif return (-1); // no slot found }
int32_t ecm_ratelimit_check(struct s_reader *reader, ECM_REQUEST *er, int32_t reader_mode) // If reader_mode is 1, ECM_REQUEST need to be assigned to reader and slot. // Else just report if a free slot is available. { // No rate limit set if(!reader->ratelimitecm) { return OK; } int32_t foundspace = -1, h, maxslots = MAXECMRATELIMIT; //init slots to oscam global maximums struct ecmrl rl; struct timeb now; rl = get_ratelimit(er); if(rl.ratelimitecm > 0) { cs_debug_mask(D_CLIENT, "ratelimit found for CAID: %04X PROVID: %06X SRVID: %04X CHID: %04X maxecms: %d cycle: %d ms srvidhold: %d ms", rl.caid, rl.provid, rl.srvid, rl.chid, rl.ratelimitecm, rl.ratelimittime, rl.srvidholdtime); } else // nothing found: apply general reader limits { rl.ratelimitecm = reader->ratelimitecm; rl.ratelimittime = reader->ratelimittime; rl.srvidholdtime = reader->srvidholdtime; rl.caid = er->caid; rl.provid = er->prid; rl.chid = er->chid; rl.srvid = er->srvid; cs_debug_mask(D_CLIENT, "ratelimiter apply readerdefault for CAID: %04X PROVID: %06X SRVID: %04X CHID: %04X maxecms: %d cycle: %d ms srvidhold: %d ms", rl.caid, rl.provid, rl.srvid, rl.chid, rl.ratelimitecm, rl.ratelimittime, rl.srvidholdtime); } // Below this line: rate limit functionality. // No cooldown set if(!reader->cooldown[0]) { cs_debug_mask(D_CLIENT, "ratelimiter find a slot for srvid %04X on reader %s", er->srvid, reader->label); foundspace = ecm_ratelimit_findspace(reader, er, rl, reader_mode); if(foundspace < 0) { if(reader_mode) { if(foundspace != -2) { cs_debug_mask(D_CLIENT, "ratelimiter no free slot for srvid %04X on reader %s -> dropping!", er->srvid, reader->label); write_ecm_answer(reader, er, E_NOTFOUND, E2_RATELIMIT, NULL, "Ratelimiter: no slots free!"); } } return ERROR; // not even trowing an error... obvious reason ;) } else //we are within ecmratelimits { if(reader_mode) { // Register new slot //reader->rlecmh[foundspace].srvid=er->srvid; // register srvid reader->rlecmh[foundspace] = rl; // register this srvid ratelimit params cs_ftime(&reader->rlecmh[foundspace].last); // register request time memcpy(reader->rlecmh[foundspace].ecmd5, er->ecmd5, CS_ECMSTORESIZE);// register ecmhash reader->rlecmh[foundspace].kindecm = er->ecm[0]; // register kind of ecm } return OK; } } // Below this line: rate limit functionality with cooldown option. // Cooldown state cycle: // state = 0: Cooldown setup phase. No rate limit set. // If number of ecm request exceed reader->ratelimitecm, cooldownstate goes to 2. // state = 2: Cooldown delay phase. No rate limit set. // If number of ecm request still exceed reader->ratelimitecm at end of cooldown delay phase, // cooldownstate goes to 1 (rate limit phase). // Else return back to setup phase (state 0). // state = 1: Cooldown ratelimit phase. Rate limit set. // If cooldowntime reader->cooldown[1] is elapsed, return to cooldown setup phase (state 0). cs_ftime(&now); int32_t gone = comp_timeb(&now, &reader->cooldowntime); if(reader->cooldownstate == 1) // Cooldown in ratelimit phase { if(gone <= reader->cooldown[1]*1000) // check if cooldowntime is elapsed { maxslots = reader->ratelimitecm; } // use user defined ratelimitecm else // Cooldown time is elapsed { reader->cooldownstate = 0; // set cooldown setup phase reader->cooldowntime.time = -1; // reset cooldowntime maxslots = MAXECMRATELIMIT; //use oscam defined max slots cs_log("Reader: %s ratelimiter returning to setup phase cooling down period of %d seconds is done!", reader->label, reader->cooldown[1]); } } // if cooldownstate == 1 if(reader->cooldownstate == 2 && gone > reader->cooldown[0]*1000) { // Need to check if the otherslots are not exceeding the ratelimit at the moment that // cooldown[0] time was exceeded! // time_t actualtime = reader->cooldowntime + reader->cooldown[0]; maxslots = 0; // maxslots is used as counter for(h = 0; h < MAXECMRATELIMIT; h++) { if(reader->rlecmh[h].last.time == -1) { continue; } // skip empty slots // how many active slots are registered at end of cooldown delay period gone = comp_timeb(&now, &reader->rlecmh[h].last); if(gone <= reader->ratelimittime) { maxslots++; if(maxslots >= reader->ratelimitecm) { break; } // Need to go cooling down phase } } if(maxslots < reader->ratelimitecm) { reader->cooldownstate = 0; // set cooldown setup phase reader->cooldowntime.time = -1; // reset cooldowntime maxslots = MAXECMRATELIMIT; // maxslots is maxslots again cs_log("Reader: %s ratelimiter returning to setup phase after %d seconds cooldowndelay!", reader->label, reader->cooldown[0]); } else { reader->cooldownstate = 1; // Entering ratelimit for cooldown ratelimitseconds cs_ftime(&reader->cooldowntime); // set time to enforce ecmratelimit for defined cooldowntime maxslots = reader->ratelimitecm; // maxslots is maxslots again sort_ecmrl(reader); // keep youngest ecm requests in list + housekeeping cs_log("Reader: %s ratelimiter starting cooling down period of %d seconds!", reader->label, reader->cooldown[1]); } } // if cooldownstate == 2 cs_debug_mask(D_CLIENT, "ratelimiter cooldownphase %d find a slot for srvid %04X on reader %s", reader->cooldownstate, er->srvid, reader->label); foundspace = ecm_ratelimit_findspace(reader, er, rl, reader_mode); if(foundspace < 0) { if(reader_mode) { if(foundspace != -2) { cs_debug_mask(D_CLIENT, "ratelimiter cooldownphase %d no free slot for srvid %04X on reader %s -> dropping!", reader->cooldownstate, er->srvid, reader->label); write_ecm_answer(reader, er, E_NOTFOUND, E2_RATELIMIT, NULL, "Ratelimiter: cooldown no slots free!"); } } return ERROR; // not even trowing an error... obvious reason ;) } else //we are within ecmratelimits { if(reader_mode) { // Register new slot //reader->rlecmh[foundspace].srvid=er->srvid; // register srvid reader->rlecmh[foundspace] = rl; // register this srvid ratelimit params cs_ftime(&reader->rlecmh[foundspace].last); // register request time memcpy(reader->rlecmh[foundspace].ecmd5, er->ecmd5, CS_ECMSTORESIZE);// register ecmhash reader->rlecmh[foundspace].kindecm = er->ecm[0]; // register kind of ecm } } if(reader->cooldownstate == 0 && foundspace >= reader->ratelimitecm) { if(!reader_mode) // No actual ecm request, just check { return OK; } cs_log("Reader: %s ratelimiter cooldown detected overrun ecmratelimit of %d during setup phase!", reader->label, (foundspace - reader->ratelimitecm + 1)); reader->cooldownstate = 2; // Entering cooldowndelay phase cs_ftime(&reader->cooldowntime); // Set cooldowntime to calculate delay cs_debug_mask(D_CLIENT, "ratelimiter cooldowndelaying %d seconds", reader->cooldown[0]); } // Cooldown state housekeeping is done. There is a slot available. if(reader_mode) { // Register new slot //reader->rlecmh[foundspace].srvid=er->srvid; // register srvid reader->rlecmh[foundspace] = rl; // register this srvid ratelimit params cs_ftime(&reader->rlecmh[foundspace].last); // register request time memcpy(reader->rlecmh[foundspace].ecmd5, er->ecmd5, CS_ECMSTORESIZE);// register ecmhash reader->rlecmh[foundspace].kindecm = er->ecm[0]; // register kind of ecm } return OK; }
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]; pthread_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 = 1024; } 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)) { pthread_mutex_lock(&cl->thread_lock); cl->thread_active = 0; pthread_mutex_unlock(&cl->thread_lock); cs_debug_mask(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_debug_mask(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 pthread_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_debug_mask(D_TRACE, "start next job from list action=%d", data->action); } pthread_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; pthread_mutex_lock(&cl->thread_lock); cl->thread_active = 2; pthread_mutex_unlock(&cl->thread_lock); rc = poll(pfd, 1, 3000); pthread_mutex_lock(&cl->thread_lock); cl->thread_active = 1; pthread_mutex_unlock(&cl->thread_lock); if(rc > 0) { cs_ftime(&end); // register end time cs_debug_mask(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); int32_t gone = comp_timeb(&actualtime, &data->time); if(data != &tmp_data && gone > (int) cfg.ctimeout+1000) { cs_debug_mask(D_TRACE, "dropping client data for %s time %dms", 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_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_CACHEEX_TIMEOUT: #ifdef CS_CACHEEX cacheex_timeout(data->ptr); #endif 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: { #ifdef CS_CACHEEX ECM_REQUEST *er = data->ptr; int32_t res = 0, stats = -1; // cc-nodeid-list-check if(reader) { if(reader->ph.c_cache_push_chk && !reader->ph.c_cache_push_chk(cl, er)) { break; } res = reader->ph.c_cache_push(cl, er); stats = cacheex_add_stats(cl, er->caid, er->srvid, er->prid, 0); } else { if(module->c_cache_push_chk && !module->c_cache_push_chk(cl, er)) { break; } res = module->c_cache_push(cl, er); } debug_ecm(D_CACHEEX, "pushed ECM %s to %s res %d stats %d", buf, username(cl), res, stats); cl->cwcacheexpush++; if(cl->account) { cl->account->cwcacheexpush++; } first_client->cwcacheexpush++; #endif break; } case ACTION_CLIENT_KILL: cl->kill = 1; break; case ACTION_CLIENT_SEND_MSG: { #ifdef MODULE_CCCAM struct s_clientmsg *clientmsg = (struct s_clientmsg *)data->ptr; cc_cmd_send(cl, clientmsg->msg, clientmsg->len, clientmsg->cmd); #endif break; } } // switch __free_job_data(cl, data); } if(thread_pipe[1] && (mbuf[0] != 0x00)) { cs_ddump_mask(D_TRACE, mbuf, 1, "[OSCAM-WORK] Write to pipe:"); if(write(thread_pipe[1], mbuf, 1) == -1) // wakeup client check { cs_debug_mask(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 pthread_mutex_lock(&cl->thread_lock); if(cl->joblist && ll_count(cl->joblist) > 0) { pthread_mutex_unlock(&cl->thread_lock); continue; } else { cl->thread_active = 0; pthread_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; }
void cleanup_cache(void){ ECMHASH *ecmhash; CW *cw; struct s_pushclient *pc, *nxt; node *i,*i_next,*j,*j_next; struct timeb now; int32_t gone_first, gone_upd; pthread_rwlock_wrlock(&cache_lock); i = get_first_node_list(&ll_cache); while (i) { i_next = i->next; ecmhash = get_data_from_node(i); cs_ftime(&now); gone_first = comp_timeb(&now, &ecmhash->first_recv_time); gone_upd = comp_timeb(&now, &ecmhash->upd_time); if(ecmhash && gone_first<=(cfg.max_cache_time*1000)){ //not continue, useless check for nexts one! break; } if(ecmhash && gone_upd>(cfg.max_cache_time*1000)){ j = get_first_node_list(&ecmhash->ll_cw); while (j) { j_next = j->next; cw = get_data_from_node(j); if(cw){ pthread_rwlock_destroy(&cw->pushout_client_lock); pc = cw->pushout_client; cw->pushout_client=NULL; while (pc) { nxt = pc->next_push; NULLFREE(pc); pc = nxt; } remove_elem_list(&ecmhash->ll_cw, &cw->ll_node); remove_elem_hash_table(&ecmhash->ht_cw, &cw->ht_node); NULLFREE(cw); } j = j_next; } deinitialize_hash_table(&ecmhash->ht_cw); remove_elem_list(&ll_cache, &ecmhash->ll_node); remove_elem_hash_table(&ht_cache, &ecmhash->ht_node); NULLFREE(ecmhash); } i = i_next; } pthread_rwlock_unlock(&cache_lock); }
static int32_t ecm_ratelimit_findspace(struct s_reader *reader, ECM_REQUEST *er, struct ecmrl rl, int32_t reader_mode) { int32_t h, foundspace = -1; int32_t maxecms = MAXECMRATELIMIT; // init maxecms int32_t totalecms = 0; // init totalecms struct timeb actualtime; cs_ftime(&actualtime); for(h = 0; h < MAXECMRATELIMIT; h++) // release slots with srvid that are overtime, even if not called from reader module to maximize available slots! { if(reader->rlecmh[h].last.time == -1) { continue; } int64_t gone = comp_timeb(&actualtime, &reader->rlecmh[h].last); if( gone >= (reader->rlecmh[h].ratelimittime + reader->rlecmh[h].srvidholdtime) || gone < 0) // gone <0 fixup for bad systemtime on dvb receivers while changing transponders { cs_log_dbg(D_CLIENT, "ratelimiter srvid %04X released from slot %d/%d of reader %s (%"PRId64">=%d ratelimit ms + %d ms srvidhold!)", reader->rlecmh[h].srvid, h + 1, MAXECMRATELIMIT, reader->label, gone, reader->rlecmh[h].ratelimittime, reader->rlecmh[h].srvidholdtime); reader->rlecmh[h].last.time = -1; reader->rlecmh[h].srvid = -1; reader->rlecmh[h].kindecm = 0; reader->rlecmh[h].once = 0; } if(reader->rlecmh[h].last.time == -1) { continue; } if(reader->rlecmh[h].ratelimitecm < maxecms) { maxecms = reader->rlecmh[h].ratelimitecm; } // we found a more critical ratelimit srvid totalecms++; } cs_log_dbg(D_CLIENT, "ratelimiter found total of %d srvid for reader %s most critical is limited to %d requests", totalecms, reader->label, maxecms); if(reader->cooldown[0] && reader->cooldownstate != 1) { maxecms = MAXECMRATELIMIT; } // dont apply ratelimits if cooldown isnt in use or not in effect for(h = 0; h < MAXECMRATELIMIT; h++) // check if srvid is already in a slot { if(reader->rlecmh[h].last.time == -1) { continue; } if(reader->rlecmh[h].srvid == er->srvid && reader->rlecmh[h].caid == rl.caid && reader->rlecmh[h].provid == rl.provid && (!reader->rlecmh[h].chid || (reader->rlecmh[h].chid == rl.chid))) { int64_t gone = comp_timeb(&actualtime, &reader->rlecmh[h].last); cs_log_dbg(D_CLIENT, "ratelimiter found srvid %04X for %"PRId64" ms in slot %d/%d of reader %s", er->srvid, gone, h + 1, MAXECMRATELIMIT, reader->label); // check ecmunique if enabled and ecmunique time is done if(reader_mode && reader->ecmunique) { gone = comp_timeb(&actualtime, &reader->rlecmh[h].last); if(gone < reader->ratelimittime) { if(memcmp(reader->rlecmh[h].ecmd5, er->ecmd5, CS_ECMSTORESIZE)) //some boxes seem to send different ecms but asking in fact for same cw! { // different ecm request than one in the slot! if(er->ecm[0] == reader->rlecmh[h].kindecm) { // same ecm type! char ecmd5[17 * 3]; cs_hexdump(0, reader->rlecmh[h].ecmd5, 16, ecmd5, sizeof(ecmd5)); cs_log_dbg(D_CLIENT, "ratelimiter ecm %s in this slot for next %d ms!", ecmd5, (int)(reader->rlecmh[h].ratelimittime - gone)); struct ecm_request_t *erold = NULL; if(!cs_malloc(&erold, sizeof(struct ecm_request_t))) { return -2; } memcpy(erold, er, sizeof(struct ecm_request_t)); // copy ecm all memcpy(erold->ecmd5, reader->rlecmh[h].ecmd5, CS_ECMSTORESIZE); // replace md5 hash struct ecm_request_t *ecm = NULL; ecm = check_cache(erold, erold->client); //CHECK IF FOUND ECM IN CACHE NULLFREE(erold); if(ecm) //found in cache { write_ecm_answer(reader, er, ecm->rc, ecm->rcEx, ecm->cw, NULL, 0, &ecm->cw_ex); } // return controlword of the ecm sitting in the slot! else { write_ecm_answer(reader, er, E_NOTFOUND, E2_RATELIMIT, NULL, "Ratelimiter: no slots free!", 0, NULL); } NULLFREE(ecm); return -2; } } } if((er->ecm[0] != reader->rlecmh[h].kindecm) && (gone <= reader->ratelimittime)) { if(!reader->rlecmh[h].once) // 1 premature ecmtype change is allowed (useful right after zapping to a channel!) { reader->rlecmh[h].once = 1; cs_log_dbg(D_CLIENT, "ratelimiter changing slot %d srvid %04X ecmtype once from %s to %s!", h+1, er->srvid, (reader->rlecmh[h].kindecm == 0x80 ? "even":"odd"), (er->ecm[0] == 0x80 ? "even":"odd")); } else { cs_log_dbg(D_CLIENT, "ratelimiter srvid %04X only allowing ecmtype %s for next %d ms in slot %d/%d of reader %s -> skipping this slot!", reader->rlecmh[h].srvid, (reader->rlecmh[h].kindecm == 0x80 ? "even" : "odd"), (int)(reader->rlecmh[h].ratelimittime - gone), h + 1, maxecms, reader->label); continue; } } } if(h > 0) { for(foundspace = 0; foundspace < h; foundspace++) // check for free lower slot { if(reader->rlecmh[foundspace].last.time == -1) { reader->rlecmh[foundspace] = reader->rlecmh[h]; // replace ecm request info reader->rlecmh[h].last.time = -1; reader->rlecmh[h].srvid = -1; reader->rlecmh[h].kindecm = 0; reader->rlecmh[h].once = 0; if(foundspace < maxecms) { cs_log_dbg(D_CLIENT, "ratelimiter moved srvid %04X to slot %d/%d of reader %s", er->srvid, foundspace + 1, maxecms, reader->label); return foundspace; // moving to lower free slot! } else { cs_log_dbg(D_CLIENT, "ratelimiter removed srvid %04X from slot %d/%d of reader %s", er->srvid, foundspace + 1, maxecms, reader->label); reader->rlecmh[foundspace].last.time = -1; // free this slot since we are over ratelimit! return -1; // sorry, ratelimit! } } } } if(h < maxecms) // found but cant move to lower position! { return h; // return position if within ratelimits! } else { reader->rlecmh[h].last.time = -1; // free this slot since we are over ratelimit! reader->rlecmh[h].srvid = -1; reader->rlecmh[h].kindecm = 0; reader->rlecmh[h].once = 0; cs_log_dbg(D_CLIENT, "ratelimiter removed srvid %04X from slot %d/%d of reader %s", er->srvid, h + 1, maxecms, reader->label); return -1; // sorry, ratelimit! } } } // srvid not found in slots! // do we use cooldown at all, are we in cooldown fase? if((reader->cooldown[0] && reader->cooldownstate == 1) || !reader->cooldown[0]) { // we are in cooldown or no cooldown configured! if(totalecms + 1 > maxecms || totalecms + 1 > rl.ratelimitecm) // check if this channel fits in! { cs_log_dbg(D_CLIENT, "ratelimiter for reader %s has no free slots!", reader->label); return -1; } } else { maxecms = MAXECMRATELIMIT; // no limits right now! } for(h = 0; h < maxecms; h++) // check for free slot { if(reader->rlecmh[h].last.time == -1) { if(reader_mode) { cs_log_dbg(D_CLIENT, "ratelimiter added srvid %04X to slot %d/%d of reader %s", er->srvid, h + 1, maxecms, reader->label); } return h; // free slot found -> assign it! } else { int64_t gone = comp_timeb(&actualtime, &reader->rlecmh[h].last); cs_log_dbg(D_CLIENT, "ratelimiter srvid %04X for %"PRId64" ms present in slot %d/%d of reader %s", reader->rlecmh[h].srvid, gone , h + 1, maxecms, reader->label); } //occupied slots } foundspace = dvbapi_override_prio(reader, er, maxecms, &actualtime); if (foundspace > -1) return foundspace; return (-1); // no slot found }
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; }
bool IO_Serial_Read(struct s_reader *reader, uint32_t delay, uint32_t timeout, uint32_t size, unsigned char *data) { uint32_t count = 0; if(timeout == 0) // General fix for readers not communicating timeout and delay { if(reader->read_timeout != 0) { timeout = reader->read_timeout; } else { timeout = 9990000; } // hope 99990000 is long enough! rdr_debug_mask(reader, D_DEVICE, "Warning: read timeout 0 changed to %d us", timeout); } rdr_debug_mask(reader, D_DEVICE, "Read timeout %d us, read delay %d us, to read %d char(s), chunksize %d char(s)", timeout, delay, size, size); #if defined(WITH_STAPI) || defined(__SH4__) //internal stapi and sh4 readers need special treatment as they don't respond correctly to poll and some sh4 boxes only can read 1 byte at once if(reader->typ == R_INTERNAL) { int32_t readed; #if defined(WITH_STAPI) const uint32_t chunksize = INT_MAX; #elif defined(__SH4__) const uint32_t chunksize = 1; #endif struct timeb start, end; cs_ftime(&start); end = start; int64_t gone = 0; int32_t timeout_ms = timeout / 1000; readed = 0; while(gone < timeout_ms) { readed = read(reader->handle, &data[count], size - count >= chunksize ? chunksize : size - count); cs_ftime(&end); if(readed > 0) { count += readed; cs_ftime(&start); // Reset timer end = start; } gone = comp_timeb(&end, &start); if(count < size) { if(readed < (int32_t)chunksize) { cs_sleepus(1); } continue; } else { break; } } if(count < size) { rdr_ddump_mask(reader, D_DEVICE, data, count, "Receiving:"); return ERROR; } } else #endif // read all chars at once for all other boxes { while(count < size) { int32_t readed = -1, errorcount = 0; AGAIN: if(IO_Serial_WaitToRead(reader, delay, timeout)) { rdr_debug_mask(reader, D_DEVICE, "Timeout in IO_Serial_WaitToRead, timeout=%d us", timeout); return ERROR; } while(readed < 0 && errorcount < 10) { readed = read(reader->handle, &data[count], size - count); if(readed < 0) { if(errno == EINTR) { continue; } // try again in case of interrupt if(errno == EAGAIN) { goto AGAIN; } //EAGAIN needs select procedure again rdr_log(reader, "ERROR: %s (errno=%d %s)", __func__, errno, strerror(errno)); errorcount++; } } if(readed == 0) { rdr_ddump_mask(reader, D_DEVICE, data, count, "Receiving:"); rdr_debug_mask(reader, D_DEVICE, "Received End of transmission"); return ERROR; } count += readed; } } rdr_ddump_mask(reader, D_DEVICE, data, count, "Receiving:"); return OK; }
static int32_t cs_check_v(IN_ADDR_T ip, int32_t port, int32_t add, char *info, int32_t acosc_penalty_duration) { int32_t result = 0; bool acosc_enabled = false; #ifdef CS_ANTICASC if(cfg.acosc_enabled) acosc_enabled = true; #endif if(!(cfg.failbantime || acosc_enabled)) { return 0; } if(!cfg.v_list) { cfg.v_list = ll_create("v_list"); } struct timeb (now); cs_ftime(&now); LL_ITER itr = ll_iter_create(cfg.v_list); V_BAN *v_ban_entry; int32_t ftime = cfg.failbantime * 60 * 1000; //run over all banned entries to do housekeeping: while((v_ban_entry = ll_iter_next(&itr))) { // housekeeping: int32_t gone = comp_timeb(&now, &v_ban_entry->v_time); if(((gone >= ftime) && !v_ban_entry->acosc_entry) || (v_ban_entry->acosc_entry && ((gone/1000) >= v_ban_entry->acosc_penalty_dur))) // entry out of time->remove { NULLFREE(v_ban_entry->info); ll_iter_remove_data(&itr); continue; } if(IP_EQUAL(ip, v_ban_entry->v_ip) && port == v_ban_entry->v_port) { result = 1; if(!info) { info = v_ban_entry->info; } else if(!v_ban_entry->info) { v_ban_entry->info = cs_strdup(info); } if(!add) { if(v_ban_entry->v_count >= cfg.failbancount) { if(!v_ban_entry->acosc_entry) { cs_debug_mask(D_TRACE, "failban: banned ip %s:%d - %d seconds left%s%s", cs_inet_ntoa(v_ban_entry->v_ip), v_ban_entry->v_port, (ftime - gone)/1000, info ? ", info: " : "", info ? info : ""); } else { cs_debug_mask(D_TRACE, "failban: banned ip %s:%d - %d seconds left%s%s", cs_inet_ntoa(v_ban_entry->v_ip), v_ban_entry->v_port, (v_ban_entry->acosc_penalty_dur - (gone/1000)), info?", info: ":"", info?info:""); } } else { cs_debug_mask(D_TRACE, "failban: ip %s:%d chance %d of %d%s%s", cs_inet_ntoa(v_ban_entry->v_ip), v_ban_entry->v_port, v_ban_entry->v_count, cfg.failbancount, info ? ", info: " : "", info ? info : ""); v_ban_entry->v_count++; } } else { cs_debug_mask(D_TRACE, "failban: banned ip %s:%d - already exist in list%s%s", cs_inet_ntoa(v_ban_entry->v_ip), v_ban_entry->v_port, info ? ", info: " : "", info ? info : ""); } } } if(add && !result) { if(cs_malloc(&v_ban_entry, sizeof(V_BAN))) { cs_ftime(&v_ban_entry->v_time); v_ban_entry->v_ip = ip; v_ban_entry->v_port = port; v_ban_entry->v_count = 1; v_ban_entry->acosc_entry = false; v_ban_entry->acosc_penalty_dur = 0; if(acosc_penalty_duration > 0) { v_ban_entry->v_count = cfg.failbancount +1; // set it to a higher level v_ban_entry->acosc_entry = true; v_ban_entry->acosc_penalty_dur = acosc_penalty_duration; } if(info) { v_ban_entry->info = cs_strdup(info); } ll_iter_insert(&itr, v_ban_entry); cs_debug_mask(D_TRACE, "failban: ban ip %s:%d with timestamp %ld%s%s", cs_inet_ntoa(v_ban_entry->v_ip), v_ban_entry->v_port, v_ban_entry->v_time.time, info ? ", info: " : "", info ? info : ""); } } return result; }