/** * \brief Forks a separate simple sleep() -&- sync periodic timer * * Forks a very basic periodic timer process, that just sleep()s for * the specified interval and then calls the timer function. * The new "sync timer" process execution start immediately, the sleep() * is called first (so the first call to the timer function will happen * \<interval\> seconds after the call to fork_sync_timer) * @param child_id @see fork_process() * @param desc @see fork_process() * @param make_sock @see fork_process() * @param f timer function/callback * @param param parameter passed to the timer function * @param interval interval in seconds. * @return pid of the new process on success, -1 on error * (doesn't return anything in the child process) */ int fork_sync_timer(int child_id, char* desc, int make_sock, timer_function* f, void* param, int interval) { int pid; ticks_t ts1 = 0; ticks_t ts2 = 0; pid=fork_process(child_id, desc, make_sock); if (pid<0) return -1; if (pid==0){ /* child */ interval *= 1000; /* miliseconds */ ts2 = interval; if (cfg_child_init()) return -1; for(;;){ if (ts2>interval) sleep_us(1000); /* 1 milisecond sleep to catch up */ else sleep_us(ts2*1000); /* microseconds sleep */ ts1 = get_ticks_raw(); cfg_update(); f(TICKS_TO_S(ts1), param); /* ticks in sec for compatibility with old timers */ /* adjust the next sleep duration */ ts2 = interval - TICKS_TO_MS(get_ticks_raw()) + TICKS_TO_MS(ts1); } } /* parent */ return pid; }
/** * \brief Forks a separate simple milisecond-sleep() -&- sync periodic timer * * Forks a very basic periodic timer process, that just ms-sleep()s for * the specified interval and then calls the timer function. * The new "sync timer" process execution start immediately, the ms-sleep() * is called first (so the first call to the timer function will happen * \<interval\> seconds after the call to fork_basic_utimer) * @param child_id @see fork_process() * @param desc @see fork_process() * @param make_sock @see fork_process() * @param f timer function/callback * @param param parameter passed to the timer function * @param uinterval interval in mili-seconds. * @return pid of the new process on success, -1 on error * (doesn't return anything in the child process) */ int fork_sync_utimer(int child_id, char* desc, int make_sock, utimer_function* f, void* param, int uinterval) { int pid; ticks_t ts1 = 0; ticks_t ts2 = 0; pid=fork_process(child_id, desc, make_sock); if (pid<0) return -1; if (pid==0){ /* child */ ts2 = uinterval; if (cfg_child_init()) return -1; for(;;){ if(ts2>0) sleep_us(uinterval); else sleep_us(1); ts1 = get_ticks_raw(); cfg_update(); f(TICKS_TO_MS(ts1), param); /* ticks in mili-seconds */ ts2 = uinterval - get_ticks_raw() + ts1; } } /* parent */ return pid; }
/* frees all the expired entries until either there are no more of them * or the total memory used is <= target (to free all of them use -1 for * targer) * params: target - free expired entries until no more then taget memory * is used (use 0 to free all of them) * delta - consider an entry expired if it expires after delta * ticks from now * timeout - exit after timeout ticks * * returns: number of deleted entries * This function should be called periodically from a timer */ inline static int dst_blacklist_clean_expired(unsigned int target, ticks_t delta, ticks_t timeout) { static unsigned int start=0; unsigned int h; struct dst_blst_entry** crt; struct dst_blst_entry** tmp; struct dst_blst_entry* e; ticks_t start_time; ticks_t now; int no=0; int i; now=start_time=get_ticks_raw(); for(h=start; h!=(start+DST_BLST_HASH_SIZE); h++){ i=h%DST_BLST_HASH_SIZE; if (dst_blst_hash[i].first){ LOCK_BLST(i); for (crt=&dst_blst_hash[i].first, tmp=&(*crt)->next; *crt; crt=tmp, tmp=&(*crt)->next){ e=*crt; prefetch_loc_r((*crt)->next, 1); if ((s_ticks_t)(now+delta-(*crt)->expire)>=0){ *crt=(*crt)->next; tmp=crt; *blst_mem_used-=DST_BLST_ENTRY_SIZE(*e); blst_destroy_entry(e); BLST_HASH_STATS_DEC(i); no++; if (*blst_mem_used<=target){ UNLOCK_BLST(i); goto skip; } } } UNLOCK_BLST(i); /* check for timeout only "between" hash cells */ now=get_ticks_raw(); if ((now-start_time)>=timeout){ DBG("_dst_blacklist_clean_expired_unsafe: timeout: %d > %d\n", TICKS_TO_MS(now-start_time), TICKS_TO_MS(timeout)); goto skip; } } } skip: start=h; /* next time we start where we left */ if (no){ DBG("dst_blacklist_clean_expired, %d entries removed\n", no); } return no; }
/* dumps the content of the blacklist in a human-readable format */ void dst_blst_view(rpc_t* rpc, void* ctx) { int h; int expires; struct dst_blst_entry* e; ticks_t now; struct ip_addr ip; if (!cfg_get(core, core_cfg, use_dst_blacklist)){ rpc->fault(ctx, 500, "dst blacklist support disabled"); return; } now=get_ticks_raw(); for(h=0; h<DST_BLST_HASH_SIZE; h++) { LOCK_BLST(h); for(e=dst_blst_hash[h].first; e; e=e->next) { expires = (s_ticks_t)(now-e->expire)<=0? TICKS_TO_S(e->expire-now): -TICKS_TO_S(now-e->expire); /* don't include expired entries into view report */ if (expires < 0) { continue; } dst_blst_entry2ip(&ip, e); rpc->printf(ctx, "{\n protocol: %s", get_proto_name(e->proto)); rpc->printf(ctx, " ip: %s", ip_addr2a(&ip)); rpc->printf(ctx, " port: %d", e->port); rpc->printf(ctx, " expires in (s): %d", expires); rpc->printf(ctx, " flags: %d\n}", e->flags); } UNLOCK_BLST(h); } }
/* only for debugging, it helds the lock too long for "production" use */ void dst_blst_debug(rpc_t* rpc, void* ctx) { int h; struct dst_blst_entry* e; ticks_t now; struct ip_addr ip; if (!cfg_get(core, core_cfg, use_dst_blacklist)){ rpc->fault(ctx, 500, "dst blacklist support disabled"); return; } now=get_ticks_raw(); for(h=0; h<DST_BLST_HASH_SIZE; h++){ LOCK_BLST(h); for(e=dst_blst_hash[h].first; e; e=e->next){ dst_blst_entry2ip(&ip, e); rpc->add(ctx, "ssddd", get_proto_name(e->proto), ip_addr2a(&ip), e->port, (s_ticks_t)(now-e->expire)<=0? TICKS_TO_S(e->expire-now): -TICKS_TO_S(now-e->expire) , e->flags); } UNLOCK_BLST(h); } }
/** init tls specific data in a tcp connection. * Called when a new tcp connection is accepted or connected. * It completes the tcp connection initialisation by setting the tls * specific parts. * Note that ssl context creation and other expensive operation are left * out (they are delayed until the first read/write). * No locking is needed (when the connection is created no other process * can access it). * @param c - tcp connection. * @param sock - socket (unused for now). * @return 0 on success, < 0 on error. */ int tls_h_tcpconn_init(struct tcp_connection *c, int sock) { c->type = PROTO_TLS; c->rcv.proto = PROTO_TLS; c->timeout = get_ticks_raw() + cfg_get(tls, tls_cfg, con_lifetime); c->lifetime = cfg_get(tls, tls_cfg, con_lifetime); c->extra_data = 0; return 0; }
inline static void tcp_reader_timer_run(void) { ticks_t ticks; ticks=get_ticks_raw(); if (unlikely((ticks-tcp_reader_prev_ticks)<TCPCONN_TIMEOUT_MIN_RUN)) return; tcp_reader_prev_ticks=ticks; local_timer_run(&tcp_reader_ltimer, ticks); }
static inline int db_do_submit_query(const db1_con_t* _h, const str *_query, int (*submit_query)(const db1_con_t*, const str*)) { int ret; unsigned int ms = 0; if(unlikely(cfg_get(core, core_cfg, latency_limit_action)>0)) ms = TICKS_TO_MS(get_ticks_raw()); ret = submit_query(_h, _query); if(unlikely(cfg_get(core, core_cfg, latency_limit_action)>0)) { ms = TICKS_TO_MS(get_ticks_raw()) - ms; if(ms >= cfg_get(core, core_cfg, latency_limit_action)) { LOG(cfg_get(core, core_cfg, latency_log), "alert - query execution too long [%u ms] for [%.*s]\n", ms, _query->len<50?_query->len:50, _query->s); } } return ret; }
int tcpops_set_connection_lifetime(struct tcp_connection* con, int time) { if (unlikely(con == NULL)) { LM_CRIT("BUG: con == NULL"); return -1; } if (unlikely(time < 0)) { LM_ERR("Invalid timeout value, %d, must be >= 0\n", time); return -1; } con->lifetime = S_TO_TICKS(time); con->timeout = get_ticks_raw() + con->lifetime; LM_DBG("new connection lifetime for conid=%d: %d\n", con->id, con->timeout); return 1; }
/** * \brief Forks a timer process based on the local timer * * Forks a separate timer process running a local_timer.h type of timer * A pointer to the local_timer handle (allocated in shared memory) is * returned in lt_h. It can be used to add/delete more timers at runtime * (via local_timer_add()/local_timer_del() a.s.o). * If timers are added from separate processes, some form of locking must be * used (all the calls to local_timer* must be enclosed by locks if it * cannot be guaranteed that they cannot execute in the same time) * The timer "engine" must be run manually from the child process. For * example a very simple local timer process that just runs a single * periodic timer can be started in the following way: * struct local_timer* lt_h; * * pid=fork_local_timer_process(...., <_h); * if (pid==0){ * timer_init(&my_timer, my_timer_f, 0, 0); * local_timer_add(<_h, &my_timer, S_TO_TICKS(10), get_ticks_raw()); * while(1) { sleep(1); local_timer_run(lt, get_ticks_raw()); } * } * * @param child_id @see fork_process() * @param desc @see fork_process() * @param make_sock @see fork_process() * @param lt_h local_timer handler * @return pid to the parent, 0 to the child, -1 if error. */ int fork_local_timer_process(int child_id, char* desc, int make_sock, struct local_timer** lt_h) { int pid; struct local_timer* lt; lt=shm_malloc(sizeof(*lt)); if (lt==0) goto error; if (init_local_timer(lt, get_ticks_raw())<0) goto error; pid=fork_process(child_id, desc, make_sock); if (pid<0) goto error; *lt_h=lt; return pid; error: if (lt) shm_free(lt); return -1; }
void wsconn_close_now(ws_connection_t *wsc) { struct tcp_connection *con = tcpconn_get(wsc->id, 0, 0, 0, 0); if (wsconn_rm(wsc, WSCONN_EVENTROUTE_YES) < 0) LM_ERR("removing WebSocket connection\n"); if (con == NULL) { LM_ERR("getting TCP/TLS connection\n"); return; } tcpconn_put(con); con->send_flags.f |= SND_F_CON_CLOSE; con->state = S_CONN_BAD; con->timeout = get_ticks_raw(); }
/* returns 1 if the entry was deleted, 0 if not found */ int dst_blacklist_del(struct dest_info* si, struct sip_msg* msg) { unsigned short hash; struct ip_addr ip; ticks_t now; int ret; unsigned short port; ret=0; su2ip_addr(&ip, &si->to); port=su_getport(&si->to); now=get_ticks_raw(); hash=dst_blst_hash_no(si->proto, &ip, port); if (unlikely(dst_blst_hash[hash].first)){ LOCK_BLST(hash); ret=_dst_blacklist_del(hash, &ip, si->proto, port, now); UNLOCK_BLST(hash); } return ret; }
/** * \brief Forks a separate simple milisecond-sleep() periodic timer * * Forks a very basic periodic timer process, that just ms-sleep()s for * the specified interval and then calls the timer function. * The new "basic timer" process execution start immediately, the ms-sleep() * is called first (so the first call to the timer function will happen * \<interval\> seconds after the call to fork_basic_utimer) * @param child_id @see fork_process() * @param desc @see fork_process() * @param make_sock @see fork_process() * @param f timer function/callback * @param param parameter passed to the timer function * @param uinterval interval in mili-seconds. * @return pid of the new process on success, -1 on error * (doesn't return anything in the child process) */ int fork_basic_utimer(int child_id, char* desc, int make_sock, utimer_function* f, void* param, int uinterval) { int pid; ticks_t ts; pid=fork_process(child_id, desc, make_sock); if (pid<0) return -1; if (pid==0){ /* child */ if (cfg_child_init()) return -1; for(;;){ sleep_us(uinterval); cfg_update(); ts = get_ticks_raw(); f(TICKS_TO_MS(ts), param); /* ticks in mili-seconds */ } } /* parent */ return pid; }
/* if no blacklisted returns 0, else returns the blacklist flags */ inline static int dst_is_blacklisted_ip(unsigned char proto, struct ip_addr* ip, unsigned short port) { struct dst_blst_entry* e; unsigned short hash; ticks_t now; int ret; ret=0; now=get_ticks_raw(); hash=dst_blst_hash_no(proto, ip, port); if (unlikely(dst_blst_hash[hash].first)){ LOCK_BLST(hash); e=_dst_blacklist_lst_find(hash, ip, proto, port, now); if (e){ ret=e->flags; } UNLOCK_BLST(hash); } return ret; }
void tcp_receive_loop(int unix_sock) { /* init */ tcpmain_sock=unix_sock; /* init com. socket */ if (init_io_wait(&io_w, get_max_open_fds(), tcp_poll_method)<0) goto error; tcp_reader_prev_ticks=get_ticks_raw(); if (init_local_timer(&tcp_reader_ltimer, get_ticks_raw())!=0) goto error; /* add the unix socket */ if (io_watch_add(&io_w, tcpmain_sock, POLLIN, F_TCPMAIN, 0)<0){ LM_CRIT("failed to add socket to the fd list\n"); goto error; } /* initialize the config framework */ if (cfg_child_init()) goto error; /* main loop */ switch(io_w.poll_method){ case POLL_POLL: while(1){ io_wait_loop_poll(&io_w, TCP_CHILD_SELECT_TIMEOUT, 0); tcp_reader_timer_run(); } break; #ifdef HAVE_SELECT case POLL_SELECT: while(1){ io_wait_loop_select(&io_w, TCP_CHILD_SELECT_TIMEOUT, 0); tcp_reader_timer_run(); } break; #endif #ifdef HAVE_SIGIO_RT case POLL_SIGIO_RT: while(1){ io_wait_loop_sigio_rt(&io_w, TCP_CHILD_SELECT_TIMEOUT); tcp_reader_timer_run(); } break; #endif #ifdef HAVE_EPOLL case POLL_EPOLL_LT: while(1){ io_wait_loop_epoll(&io_w, TCP_CHILD_SELECT_TIMEOUT, 0); tcp_reader_timer_run(); } break; case POLL_EPOLL_ET: while(1){ io_wait_loop_epoll(&io_w, TCP_CHILD_SELECT_TIMEOUT, 1); tcp_reader_timer_run(); } break; #endif #ifdef HAVE_KQUEUE case POLL_KQUEUE: while(1){ io_wait_loop_kqueue(&io_w, TCP_CHILD_SELECT_TIMEOUT, 0); tcp_reader_timer_run(); } break; #endif #ifdef HAVE_DEVPOLL case POLL_DEVPOLL: while(1){ io_wait_loop_devpoll(&io_w, TCP_CHILD_SELECT_TIMEOUT, 0); tcp_reader_timer_run(); } break; #endif default: LM_CRIT("no support for poll method %s (%d)\n", poll_method_name(io_w.poll_method), io_w.poll_method); goto error; } error: destroy_io_wait(&io_w); LM_CRIT("exiting..."); exit(-1); }
/* adds a proto ip:port combination to the blacklist * returns 0 on success, -1 on error (blacklist full -- would use more then * blst:_max_mem, or out of shm. mem.) */ inline static int dst_blacklist_add_ip(unsigned char err_flags, unsigned char proto, struct ip_addr* ip, unsigned short port, ticks_t timeout) { int size; struct dst_blst_entry* e; unsigned short hash; ticks_t now; int ret; ret=0; if (ip->af==AF_INET){ err_flags&=~BLST_IS_IPV6; /* make sure the ipv6 flag is reset */ size=sizeof(struct dst_blst_entry); }else{ err_flags|=BLST_IS_IPV6; size=sizeof(struct dst_blst_entry)+12 /* ipv6 addr - 4 */; } now=get_ticks_raw(); hash=dst_blst_hash_no(proto, ip, port); /* check if the entry already exists */ LOCK_BLST(hash); e=_dst_blacklist_lst_find(hash, ip, proto, port, now); if (e){ e->flags|=err_flags; e->expire=now+timeout; /* update the timeout */ }else{ if (unlikely((*blst_mem_used+size) >= cfg_get(core, core_cfg, blst_max_mem))){ #ifdef USE_DST_BLACKLIST_STATS dst_blacklist_stats[process_no].bkl_lru_cnt++; #endif UNLOCK_BLST(hash); /* first try to free some memory (~ 12%), but don't * spend more then 250 ms*/ dst_blacklist_clean_expired(*blst_mem_used/16*14, 0, MS_TO_TICKS(250)); if (unlikely(*blst_mem_used+size >= cfg_get(core, core_cfg, blst_max_mem))){ ret=-1; goto error; } LOCK_BLST(hash); } e=shm_malloc(size); if (e==0){ UNLOCK_BLST(hash); ret=E_OUT_OF_MEM; goto error; } *blst_mem_used+=size; e->flags=err_flags; e->proto=proto; e->port=port; memcpy(e->ip, ip->u.addr, ip->len); e->expire=now+timeout; /* update the timeout */ e->next=0; dst_blacklist_lst_add(&dst_blst_hash[hash].first, e); BLST_HASH_STATS_INC(hash); } UNLOCK_BLST(hash); error: return ret; }
/* WARNING: - dst_cell contains the created cell, but it is un-referenced * (before using it make sure you REF() it first) * - if ACK (method==ACK), a cell will be created but it will not * be added in the hash table (should be either deleted by the * caller) */ static inline int t_uac_prepare(uac_req_t *uac_r, struct retr_buf **dst_req, struct cell **dst_cell) { struct dest_info dst; struct cell *new_cell; struct retr_buf *request; char* buf; int buf_len, ret; unsigned int hi; int is_ack; ticks_t lifetime; #ifdef USE_DNS_FAILOVER struct dns_srv_handle dns_h; #endif long nhtype; #ifdef WITH_EVENT_LOCAL_REQUEST struct cell *backup_t; int backup_branch; unsigned int backup_msgid; static struct sip_msg lreq; char *buf1; int buf_len1; int sflag_bk; int backup_route_type; #endif snd_flags_t snd_flags; tm_xlinks_t backup_xd; tm_xdata_t local_xd; ret=-1; hi=0; /* make gcc happy */ /*if (dst_req) *dst_req = NULL;*/ is_ack = (((uac_r->method->len == 3) && (memcmp("ACK", uac_r->method->s, 3)==0)) ? 1 : 0); /*** added by dcm * - needed by external ua to send a request within a dlg */ if ((nhtype = w_calculate_hooks(uac_r->dialog)) < 0) /* if err's returned, the message is incorrect */ goto error2; if (!uac_r->dialog->loc_seq.is_set) { /* this is the first request in the dialog, set cseq to default value now - Miklos */ uac_r->dialog->loc_seq.value = DEFAULT_CSEQ; uac_r->dialog->loc_seq.is_set = 1; } DBG("DEBUG:tm:t_uac: next_hop=<%.*s>\n",uac_r->dialog->hooks.next_hop->len, uac_r->dialog->hooks.next_hop->s); /* new message => take the dialog send_socket if set, or the default send_socket if not*/ SND_FLAGS_INIT(&snd_flags); #ifdef USE_DNS_FAILOVER if (cfg_get(core, core_cfg, use_dns_failover)){ dns_srv_handle_init(&dns_h); if ((uri2dst2(&dns_h, &dst, uac_r->dialog->send_sock, snd_flags, uac_r->dialog->hooks.next_hop, PROTO_NONE)==0) || (dst.send_sock==0)){ dns_srv_handle_put(&dns_h); ser_error = E_NO_SOCKET; ret=ser_error; LOG(L_ERR, "t_uac: no socket found\n"); goto error2; } dns_srv_handle_put(&dns_h); /* not needed anymore */ }else{ if ((uri2dst2(0, &dst, uac_r->dialog->send_sock, snd_flags, uac_r->dialog->hooks.next_hop, PROTO_NONE)==0) || (dst.send_sock==0)){ ser_error = E_NO_SOCKET; ret=ser_error; LOG(L_ERR, "t_uac: no socket found\n"); goto error2; } } #else /* USE_DNS_FAILOVER */ if ((uri2dst2(&dst, uac_r->dialog->send_sock, snd_flags, uac_r->dialog->hooks.next_hop, PROTO_NONE)==0) || (dst.send_sock==0)){ ser_error = E_NO_SOCKET; ret=ser_error; LOG(L_ERR, "t_uac: no socket found\n"); goto error2; } #endif /* USE_DNS_FAILOVER */ /* build cell sets X/AVP lists to new transaction structure * => bakup in a tmp struct and restore afterwards */ memset(&local_xd, 0, sizeof(tm_xdata_t)); tm_xdata_replace(&local_xd, &backup_xd); new_cell = build_cell(0); tm_xdata_replace(0, &backup_xd); if (!new_cell) { ret=E_OUT_OF_MEM; LOG(L_ERR, "t_uac: short of cell shmem\n"); goto error2; } if (uac_r->method->len==INVITE_LEN && memcmp(uac_r->method->s, INVITE, INVITE_LEN)==0){ new_cell->flags |= T_IS_INVITE_FLAG; new_cell->flags|=T_AUTO_INV_100 & (!cfg_get(tm, tm_cfg, tm_auto_inv_100) -1); #ifdef WITH_AS_SUPPORT if (uac_r->cb_flags & TMCB_DONT_ACK) new_cell->flags |= T_NO_AUTO_ACK; #endif lifetime=cfg_get(tm, tm_cfg, tm_max_inv_lifetime); }else lifetime=cfg_get(tm, tm_cfg, tm_max_noninv_lifetime); new_cell->flags |= T_IS_LOCAL_FLAG; /* init timers hack, new_cell->fr_timer and new_cell->fr_inv_timer * must be set, or else the fr will happen immediately * we can't call init_new_t() because we don't have a sip msg * => we'll ignore t_set_fr() or avp timer value and will use directly the * module params fr_inv_timer and fr_timer -- andrei */ new_cell->fr_timeout=cfg_get(tm, tm_cfg, fr_timeout); new_cell->fr_inv_timeout=cfg_get(tm, tm_cfg, fr_inv_timeout); new_cell->end_of_life=get_ticks_raw()+lifetime; #ifdef TM_DIFF_RT_TIMEOUT /* same as above for retransmission intervals */ new_cell->rt_t1_timeout_ms = cfg_get(tm, tm_cfg, rt_t1_timeout_ms); new_cell->rt_t2_timeout_ms = cfg_get(tm, tm_cfg, rt_t2_timeout_ms); #endif set_kr(REQ_FWDED); request = &new_cell->uac[0].request; request->dst = dst; request->flags |= nhtype; if (!is_ack) { #ifdef TM_DEL_UNREF INIT_REF(new_cell, 1); /* ref'ed only from the hash */ #endif hi=dlg2hash(uac_r->dialog); LOCK_HASH(hi); insert_into_hash_table_unsafe(new_cell, hi); UNLOCK_HASH(hi); } buf = build_uac_req(uac_r->method, uac_r->headers, uac_r->body, uac_r->dialog, 0, new_cell, &buf_len, &dst); if (!buf) { LOG(L_ERR, "t_uac: Error while building message\n"); ret=E_OUT_OF_MEM; goto error1; } #ifdef WITH_EVENT_LOCAL_REQUEST if (unlikely(goto_on_local_req>=0)) { DBG("executing event_route[tm:local-request]\n"); if(likely(build_sip_msg_from_buf(&lreq, buf, buf_len, inc_msg_no()) == 0)) { /* fill some field in sip_msg */ if (unlikely(set_dst_uri(&lreq, uac_r->dialog->hooks.next_hop))) { LM_ERR("failed to set dst_uri"); free_sip_msg(&lreq); } else { struct onsend_info onsnd_info; lreq.force_send_socket = uac_r->dialog->send_sock; lreq.rcv.proto = dst.send_sock->proto; lreq.rcv.src_ip = dst.send_sock->address; lreq.rcv.src_port = dst.send_sock->port_no; lreq.rcv.dst_port = su_getport(&dst.to); su2ip_addr(&lreq.rcv.dst_ip, &dst.to); lreq.rcv.src_su=dst.send_sock->su; lreq.rcv.bind_address=dst.send_sock; #ifdef USE_COMP lreq.rcv.comp=dst.comp; #endif /* USE_COMP */ sflag_bk = getsflags(); tm_xdata_swap(new_cell, &backup_xd, 0); onsnd_info.to=&dst.to; onsnd_info.send_sock=dst.send_sock; onsnd_info.buf=buf; onsnd_info.len=buf_len; p_onsend=&onsnd_info; /* run the route */ backup_route_type = get_route_type(); set_route_type(LOCAL_ROUTE); /* set T to the current transaction */ backup_t=get_t(); backup_branch=get_t_branch(); backup_msgid=global_msg_id; /* fake transaction and message id */ global_msg_id=lreq.id; set_t(new_cell, T_BR_UNDEFINED); run_top_route(event_rt.rlist[goto_on_local_req], &lreq, 0); /* restore original environment */ set_t(backup_t, backup_branch); global_msg_id=backup_msgid; set_route_type( backup_route_type ); p_onsend=0; /* restore original environment */ tm_xdata_swap(new_cell, &backup_xd, 1); setsflagsval(sflag_bk); if (unlikely(lreq.new_uri.s)) { pkg_free(lreq.new_uri.s); lreq.new_uri.s=0; lreq.new_uri.len=0; } if (unlikely(lreq.dst_uri.s)) { pkg_free(lreq.dst_uri.s); lreq.dst_uri.s=0; lreq.dst_uri.len=0; } if (unlikely(lreq.add_rm || lreq.body_lumps)) { LM_DBG("apply new updates to sip msg\n"); buf1 = build_req_buf_from_sip_req(&lreq, (unsigned int*)&buf_len1, &dst, BUILD_NO_LOCAL_VIA|BUILD_NO_VIA1_UPDATE| BUILD_IN_SHM); if (likely(buf1)){ shm_free(buf); buf = buf1; buf_len = buf_len1; /* a possible change of the method is not handled! */ } } lreq.buf=0; /* covers the obsolete DYN_BUF */ free_sip_msg(&lreq); } } } #endif new_cell->method.s = buf; new_cell->method.len = uac_r->method->len; request->buffer = buf; request->buffer_len = buf_len; new_cell->nr_of_outgoings++; /* Register the callbacks after everything is successful and nothing can fail. Otherwise the callback parameter would be freed twise, once from TMCB_DESTROY, and again because of the negative return code. */ if(uac_r->cb && insert_tmcb(&(new_cell->tmcb_hl), uac_r->cb_flags, *(uac_r->cb), uac_r->cbp, NULL)!=1){ ret=E_OUT_OF_MEM; LOG(L_ERR, "t_uac: short of tmcb shmem\n"); goto error1; } if (has_local_reqin_tmcbs()) run_local_reqin_callbacks(new_cell, 0, 0); #ifdef DIALOG_CALLBACKS run_trans_dlg_callbacks(uac_r->dialog, new_cell, request); #endif /* DIALOG_CALLBACKS */ if (dst_req) *dst_req = request; if (dst_cell) *dst_cell = new_cell; else if(is_ack && dst_req==0){ free_cell(new_cell); } return 1; error1: if (!is_ack) { LOCK_HASH(hi); remove_from_hash_table_unsafe(new_cell); UNLOCK_HASH(hi); #ifdef TM_DEL_UNREF UNREF_FREE(new_cell); }else #else } #endif free_cell(new_cell); error2: return ret; }
static void tls_list(rpc_t* rpc, void* c) { char buf[128]; char src_ip[IP_ADDR_MAX_STR_SIZE]; char dst_ip[IP_ADDR_MAX_STR_SIZE]; void* handle; char* tls_info; char* state; struct tls_extra_data* tls_d; struct tcp_connection* con; int i, len, timeout; TCPCONN_LOCK; for(i = 0; i < TCP_ID_HASH_SIZE; i++) { for (con = tcpconn_id_hash[i]; con; con = con->id_next) { if (con->rcv.proto != PROTO_TLS) continue; tls_d = con->extra_data; rpc->add(c, "{", &handle); /* tcp data */ if ((len = ip_addr2sbuf(&con->rcv.src_ip, src_ip, sizeof(src_ip))) == 0) BUG("failed to convert source ip"); src_ip[len] = 0; if ((len = ip_addr2sbuf(&con->rcv.dst_ip, dst_ip, sizeof(dst_ip))) == 0) BUG("failed to convert destination ip"); dst_ip[len] = 0; timeout = TICKS_TO_S(con->timeout - get_ticks_raw()); rpc->struct_add(handle, "ddsdsd", "id", con->id, "timeout", timeout, "src_ip", src_ip, "src_port", con->rcv.src_port, "dst_ip", dst_ip, "dst_port", con->rcv.dst_port); if (tls_d) { if(SSL_get_current_cipher(tls_d->ssl)) { tls_info = SSL_CIPHER_description( SSL_get_current_cipher(tls_d->ssl), buf, sizeof(buf)); len = strlen(buf); if (len && buf[len - 1] == '\n') buf[len - 1] = '\0'; } else { tls_info = "unknown"; } /* tls data */ state = "unknown/error"; lock_get(&con->write_lock); switch(tls_d->state) { case S_TLS_NONE: state = "none/init"; break; case S_TLS_ACCEPTING: state = "tls_accept"; break; case S_TLS_CONNECTING: state = "tls_connect"; break; case S_TLS_ESTABLISHED: state = "established"; break; } rpc->struct_add(handle, "sddds", "cipher", tls_info, "ct_wq_size", tls_d->ct_wq? tls_d->ct_wq->queued:0, "enc_rd_buf", tls_d->enc_rd_buf? tls_d->enc_rd_buf->size:0, "flags", tls_d->flags, "state", state ); lock_release(&con->write_lock); } else { rpc->struct_add(handle, "sddds", "cipher", "unknown", "ct_wq_size", 0, "enc_rd_buf", 0, "flags", 0, "state", "pre-init" ); } } } TCPCONN_UNLOCK; }
int timer_del_safe(struct timer_ln* tl) #endif { int ret; ret=-1; again: /* quick exit if timer inactive */ if ( !(tl->flags & F_TIMER_ACTIVE)){ #ifdef TIMER_DEBUG LOG(timerlog, "timer_del called on an inactive timer %p (%p, %p)," " flags %x\n", tl, tl->next, tl->prev, tl->flags); LOG(timerlog, "WARN: -timer_del-; called from %s(%s):%d\n", func, file, line); LOG(timerlog, "WARN: -timer_del-: added %d times" ", last from: %s(%s):%d, deleted %d times" ", last from: %s(%s):%d, init %d times, expired %d \n", tl->add_calls, tl->add_func, tl->add_file, tl->add_line, tl->del_calls, tl->del_func, tl->del_file, tl->del_line, tl->init, tl->expires_no); #else /* LM_DBG("called on an inactive timer %p (%p, %p)," " flags %x\n", tl, tl->next, tl->prev, tl->flags); */ #endif return -1; } #ifdef USE_SLOW_TIMER if (IS_ON_SLOW_LIST(tl) && (tl->slow_idx!=*t_idx)){ LOCK_SLOW_TIMER_LIST(); if (!IS_ON_SLOW_LIST(tl) || (tl->slow_idx==*t_idx)){ UNLOCK_SLOW_TIMER_LIST(); goto again; } if (IS_RUNNING_SLOW(tl)){ UNLOCK_SLOW_TIMER_LIST(); if (IS_IN_TIMER_SLOW()){ /* if somebody tries to shoot himself in the foot, * warn him and ignore the delete */ LM_CRIT("timer handle %p (s) tried to delete" " itself\n", tl); #ifdef TIMER_DEBUG LOG(timerlog, "WARN: -timer_del-: called from %s(%s):%d\n", func, file, line); LOG(timerlog, "WARN: -timer_del-: added %d times" ", last from: %s(%s):%d, deleted %d times" ", last from: %s(%s):%d, init %d times, expired %d \n", tl->add_calls, tl->add_func, tl->add_file, tl->add_line, tl->del_calls, tl->del_func, tl->del_file, tl->del_line, tl->init, tl->expires_no); #endif return -2; /* do nothing */ } sched_yield(); /* wait for it to complete */ goto again; } if (tl->next!=0){ _timer_rm_list(tl); /* detach */ tl->next=tl->prev=0; ret=0; #ifdef TIMER_DEBUG tl->del_file=file; tl->del_func=func; tl->del_line=line; tl->flags|=F_TIMER_DELETED; #endif }else{ #ifdef TIMER_DEBUG LOG(timerlog, "timer_del: (s) timer %p (%p, %p) flags %x " "already detached\n", tl, tl->next, tl->prev, tl->flags); LOG(timerlog, "WARN: -timer_del-: @%d tl=%p " "{ %p, %p, %d, %d, %p, %p, %04x, -}\n", get_ticks_raw(), tl, tl->next, tl->prev, tl->expire, tl->initial_timeout, tl->data, tl->f, tl->flags); LOG(timerlog, "WARN: -timer_del-; called from %s(%s):%d\n", func, file, line); LOG(timerlog, "WARN: -timer_del-: added %d times" ", last from: %s(%s):%d, deleted %d times" ", last from: %s(%s):%d, init %d times, expired %d \n", tl->add_calls, tl->add_func, tl->add_file, tl->add_line, tl->del_calls, tl->del_func, tl->del_file, tl->del_line, tl->init, tl->expires_no); #else /* LM_DBG("(s) timer %p (%p, %p) flags %x " "already detached\n", tl, tl->next, tl->prev, tl->flags); */ #endif ret=-1; } UNLOCK_SLOW_TIMER_LIST(); }else{ #endif LOCK_TIMER_LIST(); #ifdef USE_SLOW_TIMER if (IS_ON_SLOW_LIST(tl) && (tl->slow_idx!=*t_idx)){ UNLOCK_TIMER_LIST(); goto again; } #endif if (IS_RUNNING(tl)){ UNLOCK_TIMER_LIST(); if (IS_IN_TIMER()){ /* if somebody tries to shoot himself in the foot, * warn him and ignore the delete */ LM_CRIT("timer handle %p tried to delete" " itself\n", tl); #ifdef TIMER_DEBUG LOG(timerlog, "WARN: -timer_del-: called from %s(%s):%d\n", func, file, line); LOG(timerlog, "WARN: -timer_del-: added %d times" ", last from: %s(%s):%d, deleted %d times" ", last from: %s(%s):%d, init %d times, expired %d \n", tl->add_calls, tl->add_func, tl->add_file, tl->add_line, tl->del_calls, tl->del_func, tl->del_file, tl->del_line, tl->init, tl->expires_no); #endif return -2; /* do nothing */ } sched_yield(); /* wait for it to complete */ goto again; } if ((tl->next!=0)&&(tl->prev!=0)){ _timer_rm_list(tl); /* detach */ tl->next=tl->prev=0; ret=0; #ifdef TIMER_DEBUG tl->del_file=file; tl->del_func=func; tl->del_line=line; tl->flags|=F_TIMER_DELETED; #endif }else{ #ifdef TIMER_DEBUG LOG(timerlog, "timer_del: (f) timer %p (%p, %p) flags %x " "already detached\n", tl, tl->next, tl->prev, tl->flags); LOG(timerlog, "WARN: -timer_del-: @%d tl=%p " "{ %p, %p, %d, %d, %p, %p, %04x, -}\n", get_ticks_raw(), tl, tl->next, tl->prev, tl->expire, tl->initial_timeout, tl->data, tl->f, tl->flags); LOG(timerlog, "WARN: -timer_del-; called from %s(%s):%d\n", func, file, line); LOG(timerlog, "WARN: -timer_del-: added %d times" ", last from: %s(%s):%d, deleted %d times" ", last from: %s(%s):%d, init %d times, expired %d \n", tl->add_calls, tl->add_func, tl->add_file, tl->add_line, tl->del_calls, tl->del_func, tl->del_file, tl->del_line, tl->init, tl->expires_no); #else /* LM_DBG("(f) timer %p (%p, %p) flags %x " "already detached\n", tl, tl->next, tl->prev, tl->flags); */ #endif ret=-1; } UNLOCK_TIMER_LIST(); #ifdef USE_SLOW_TIMER } #endif return ret; }
inline static void final_response_handler( struct retr_buf *r_buf, struct cell *t) { int silent; #ifdef USE_DNS_FAILOVER /*int i; int added_branches; */ int branch_ret; int prev_branch; ticks_t now; #endif #ifdef EXTRA_DEBUG if(t->flags & T_IN_AGONY) { LM_ERR("transaction %p scheduled for deletion and" " called from FR timer (flags %x)\n", t, t->flags); abort(); } #endif /* FR for local cancels.... */ if(r_buf->rbtype == TYPE_LOCAL_CANCEL) { #ifdef TIMER_DEBUG LM_DBG("stop retr for local cancel\n"); #endif return; } /* FR for replies (negative INVITE replies) */ if(r_buf->rbtype > 0) { #ifdef EXTRA_DEBUG if(t->uas.request->REQ_METHOD != METHOD_INVITE || t->uas.status < 200) { LM_CRIT("BUG - unknown type reply buffer\n"); abort(); } #endif put_on_wait(t); return; }; /* lock reply processing to determine how to proceed reliably */ LOCK_REPLIES(t); /* now it can be only a request retransmission buffer; try if you can simply discard the local transaction state without compellingly removing it from the world */ silent = /* don't go silent if disallowed globally ... */ cfg_get(tm, tm_cfg, noisy_ctimer) == 0 /* ... or for this particular transaction */ && has_noisy_ctimer(t) == 0 /* not for UACs */ && !is_local(t) /* invites only */ && is_invite(t) /* parallel forking does not allow silent state discarding */ && t->nr_of_outgoings == 1 /* on_negativ reply handler not installed -- serial forking * could occur otherwise */ && t->on_failure == 0 /* the same for FAILURE callbacks */ && !has_tran_tmcbs(t, TMCB_ON_FAILURE_RO | TMCB_ON_FAILURE) /* something received -- we will not be silent on error */ && t->uac[r_buf->branch].last_received == 0; if(silent) { UNLOCK_REPLIES(t); #ifdef EXTRA_DEBUG LM_DBG("transaction silently dropped (%p), branch %d, last_received %d\n", t, r_buf->branch, t->uac[r_buf->branch].last_received); #endif put_on_wait(t); return; } #ifdef EXTRA_DEBUG LM_DBG("stop retr. and send CANCEL (%p)\n", t); #endif if((r_buf->branch < sr_dst_max_branches) && /* r_buf->branch is always >=0 */ (t->uac[r_buf->branch].last_received == 0) && (t->uac[r_buf->branch].request.buffer != NULL) /* not a blind UAC */ ) { /* no reply received */ #ifdef USE_DST_BLACKLIST if(r_buf->my_T && r_buf->my_T->uas.request && (r_buf->my_T->uas.request->REQ_METHOD & cfg_get(tm, tm_cfg, tm_blst_methods_add))) dst_blacklist_add( BLST_ERR_TIMEOUT, &r_buf->dst, r_buf->my_T->uas.request); #endif #ifdef USE_DNS_FAILOVER /* if this is an invite, the destination resolves to more ips, and * it still hasn't passed more than fr_inv_timeout since we * started, add another branch/uac */ if(cfg_get(core, core_cfg, use_dns_failover)) { now = get_ticks_raw(); if((s_ticks_t)(t->end_of_life - now) > 0) { branch_ret = add_uac_dns_fallback( t, t->uas.request, &t->uac[r_buf->branch], 0); prev_branch = -1; while((branch_ret >= 0) && (branch_ret != prev_branch)) { prev_branch = branch_ret; branch_ret = t_send_branch(t, branch_ret, t->uas.request, 0, 0); } } } #endif } fake_reply(t, r_buf->branch, 408); }
/* handle io routine, based on the fd_map type * (it will be called from io_wait_loop* ) * params: fm - pointer to a fd hash entry * idx - index in the fd_array (or -1 if not known) * return: -1 on error, or when we are not interested any more on reads * from this fd (e.g.: we are closing it ) * 0 on EAGAIN or when by some other way it is known that no more * io events are queued on the fd (the receive buffer is empty). * Usefull to detect when there are no more io events queued for * sigio_rt, epoll_et, kqueue. * >0 on successfull read from the fd (when there might be more io * queued -- the receive buffer might still be non-empty) */ inline static int handle_io(struct fd_map* fm, short events, int idx) { int ret; int n; int read_flags; struct tcp_connection* con; int s; long resp; ticks_t t; /* update the local config */ cfg_update(); switch(fm->type){ case F_TCPMAIN: again: ret=n=receive_fd(fm->fd, &con, sizeof(con), &s, 0); LM_DBG("received n=%d con=%p, fd=%d\n", n, con, s); if (unlikely(n<0)){ if (errno == EWOULDBLOCK || errno == EAGAIN){ ret=0; break; }else if (errno == EINTR) goto again; else{ LM_CRIT("read_fd: %s \n", strerror(errno)); abort(); /* big error*/ } } if (unlikely(n==0)){ LM_ERR("0 bytes read\n"); goto error; } if (unlikely(con==0)){ LM_CRIT("null pointer\n"); goto error; } con->fd=s; if (unlikely(s==-1)) { LM_ERR("read_fd: no fd read\n"); goto con_error; } con->reader_pid=my_pid(); if (unlikely(con==tcp_conn_lst)){ LM_CRIT("duplicate connection received: %p, id %d, fd %d, refcnt %d" " state %d (n=%d)\n", con, con->id, con->fd, atomic_get(&con->refcnt), con->state, n); goto con_error; break; /* try to recover */ } if (unlikely(con->state==S_CONN_BAD)){ LM_WARN("received an already bad connection: %p id %d refcnt %d\n", con, con->id, atomic_get(&con->refcnt)); goto con_error; } /* if we received the fd there is most likely data waiting to * be read => process it first to avoid extra sys calls */ read_flags=((con->flags & (F_CONN_EOF_SEEN|F_CONN_FORCE_EOF)) && !(con->flags & F_CONN_OOB_DATA))? RD_CONN_FORCE_EOF :0; #ifdef USE_TLS repeat_1st_read: #endif /* USE_TLS */ resp=tcp_read_req(con, &n, &read_flags); if (unlikely(resp<0)){ /* some error occured, but on the new fd, not on the tcp * main fd, so keep the ret value */ if (unlikely(resp!=CONN_EOF)) con->state=S_CONN_BAD; LM_WARN("%s:%d %s releasing\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); release_tcpconn(con, resp, tcpmain_sock); break; } #ifdef USE_TLS /* repeat read if requested (for now only tls might do this) */ if (unlikely(read_flags & RD_CONN_REPEAT_READ)) goto repeat_1st_read; #endif /* USE_TLS */ /* must be before io_watch_add, io_watch_add might catch some * already existing events => might call handle_io and * handle_io might decide to del. the new connection => * must be in the list */ tcpconn_listadd(tcp_conn_lst, con, c_next, c_prev); t=get_ticks_raw(); con->timeout=t+S_TO_TICKS(TCP_CHILD_TIMEOUT); /* re-activate the timer */ con->timer.f=tcpconn_read_timeout; local_timer_reinit(&con->timer); local_timer_add(&tcp_reader_ltimer, &con->timer, S_TO_TICKS(TCP_CHILD_TIMEOUT), t); if (unlikely(io_watch_add(&io_w, s, POLLIN, F_TCPCONN, con)<0)){ LM_CRIT("io_watch_add failed for %p id %d fd %d, state %d, flags %x," " main fd %d, refcnt %d\n", con, con->id, con->fd, con->state, con->flags, con->s, atomic_get(&con->refcnt)); tcpconn_listrm(tcp_conn_lst, con, c_next, c_prev); local_timer_del(&tcp_reader_ltimer, &con->timer); goto con_error; } break; case F_TCPCONN: con=(struct tcp_connection*)fm->data; if (unlikely(con->state==S_CONN_BAD)){ resp=CONN_ERROR; if (!(con->send_flags.f & SND_F_CON_CLOSE)) LM_WARN("F_TCPCONN connection marked as bad: %p id %d refcnt %d\n", con, con->id, atomic_get(&con->refcnt)); goto read_error; } read_flags=(( #ifdef POLLRDHUP (events & POLLRDHUP) | #endif /* POLLRDHUP */ (events & (POLLHUP|POLLERR)) | (con->flags & (F_CONN_EOF_SEEN|F_CONN_FORCE_EOF))) && !(events & POLLPRI))? RD_CONN_FORCE_EOF: 0; #ifdef USE_TLS repeat_read: #endif /* USE_TLS */ resp=tcp_read_req(con, &ret, &read_flags); if (unlikely(resp<0)){ read_error: ret=-1; /* some error occured */ if (unlikely(io_watch_del(&io_w, con->fd, idx, IO_FD_CLOSING) < 0)){ LM_CRIT("io_watch_del failed for %p id %d fd %d," " state %d, flags %x, main fd %d, refcnt %d\n", con, con->id, con->fd, con->state, con->flags, con->s, atomic_get(&con->refcnt)); } tcpconn_listrm(tcp_conn_lst, con, c_next, c_prev); local_timer_del(&tcp_reader_ltimer, &con->timer); if (unlikely(resp!=CONN_EOF)) con->state=S_CONN_BAD; LM_WARN("%s:%d %s releasing\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); release_tcpconn(con, resp, tcpmain_sock); }else{ #ifdef USE_TLS if (unlikely(read_flags & RD_CONN_REPEAT_READ)) goto repeat_read; #endif /* USE_TLS */ /* update timeout */ con->timeout=get_ticks_raw()+S_TO_TICKS(TCP_CHILD_TIMEOUT); /* ret= 0 (read the whole socket buffer) if short read & * !POLLPRI, bytes read otherwise */ ret&=(((read_flags & RD_CONN_SHORT_READ) && !(events & POLLPRI)) - 1); } break; case F_NONE: LM_CRIT("empty fd map %p (%d): {%d, %d, %p}\n", fm, (int)(fm-io_w.fd_hash), fm->fd, fm->type, fm->data); goto error; default: LM_CRIT("uknown fd type %d\n", fm->type); goto error; } return ret; con_error: con->state=S_CONN_BAD; LM_WARN("%s:%d %s releasing\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); release_tcpconn(con, CONN_ERROR, tcpmain_sock); return ret; error: return -1; }