/** * replicates the remote deletion of a dialog locally * by reading the relevant information using the Binary Packet Interface */ int dlg_replicated_delete(void) { str call_id, from_tag, to_tag; unsigned int dir, dst_leg; struct dlg_cell *dlg; int old_state, new_state, unref, ret; bin_pop_str(&call_id); bin_pop_str(&from_tag); bin_pop_str(&to_tag); LM_DBG("Deleting dialog with callid: %.*s\n", call_id.len, call_id.s); dlg = get_dlg(&call_id, &from_tag, &to_tag, &dir, &dst_leg); if (!dlg) { LM_ERR("dialog not found (callid: |%.*s| ftag: |%.*s|\n", call_id.len, call_id.s, from_tag.len, from_tag.s); return -1; } destroy_linkers(dlg->profile_links); dlg->profile_links = NULL; next_state_dlg(dlg, DLG_EVENT_REQBYE, &old_state, &new_state, &unref); if (old_state == new_state) { LM_ERR("duplicate dialog delete request (callid: |%.*s|" "ftag: |%.*s|\n", call_id.len, call_id.s, from_tag.len, from_tag.s); return -1; } ret = remove_dlg_timer(&dlg->tl); if (ret < 0) { LM_CRIT("unable to unlink the timer on dlg %p [%u:%u] " "with clid '%.*s' and tags '%.*s' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg->legs[DLG_CALLER_LEG].tag.len, dlg->legs[DLG_CALLER_LEG].tag.s, dlg->legs[callee_idx(dlg)].tag.len, ZSW(dlg->legs[callee_idx(dlg)].tag.s)); } else if (ret > 0) { LM_DBG("dlg expired (not in timer list) on dlg %p [%u:%u] " "with clid '%.*s' and tags '%.*s' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg->legs[DLG_CALLER_LEG].tag.len, dlg->legs[DLG_CALLER_LEG].tag.s, dlg->legs[callee_idx(dlg)].tag.len, ZSW(dlg->legs[callee_idx(dlg)].tag.s)); } else { /* dialog sucessfully removed from timer -> unref */ unref++; } unref_dlg(dlg, 1 + unref); if_update_stat(dlg_enable_stats, active_dlgs, -1); return 0; }
void destroy_dlg(struct dlg_cell *dlg) { int ret = 0; LM_DBG("destroying dialog %p\n",dlg); ret = remove_dlg_timer(&dlg->tl); if (ret < 0) { LM_CRIT("unable to unlink the timer on dlg %p [%u:%u] " "with clid '%.*s' and tags '%.*s' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg_leg_print_info( dlg, DLG_CALLER_LEG, tag), dlg_leg_print_info( dlg, callee_idx(dlg), tag)); } else if (ret > 0) { LM_DBG("dlg expired or not in list - dlg %p [%u:%u] " "with clid '%.*s' and tags '%.*s' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg_leg_print_info( dlg, DLG_CALLER_LEG, tag), dlg_leg_print_info( dlg, callee_idx(dlg), tag)); } run_dlg_callbacks( DLGCB_DESTROY , dlg, 0, DLG_DIR_NONE, NULL, 0); free_dlg_dlg(dlg); }
/* When done, this function also has the job to unref the dialog as removed * from timer list. This must be done in all cases!! */ void dlg_ontimeout( struct dlg_tl *tl) { struct dlg_cell *dlg; int new_state; int old_state; int unref; dlg = get_dlg_tl_payload(tl); LM_DBG("byeontimeout ? %d , state = %d\n",dlg->flags,dlg->state); if ( (dlg->flags&DLG_FLAG_BYEONTIMEOUT) && (dlg->state==DLG_STATE_CONFIRMED_NA || dlg->state==DLG_STATE_CONFIRMED)) { init_dlg_term_reason(dlg,"Lifetime Timeout",sizeof("Lifetime Timeout")-1); /* we just send the BYEs in both directions */ dlg_end_dlg( dlg, NULL); /* dialog is no longer refed by timer; from now one it is refed by the send_bye functions */ unref_dlg( dlg, 1); /* is not 100% sure, but do it */ if_update_stat( dlg_enable_stats, expired_dlgs, 1); return ; } next_state_dlg( dlg, DLG_EVENT_REQBYE, &old_state, &new_state, &unref); if (new_state==DLG_STATE_DELETED && old_state!=DLG_STATE_DELETED) { LM_DBG("timeout for dlg with CallID '%.*s' and tags '%.*s' '%.*s'\n", dlg->callid.len, dlg->callid.s, dlg->legs[DLG_CALLER_LEG].tag.len, dlg->legs[DLG_CALLER_LEG].tag.s, dlg->legs[callee_idx(dlg)].tag.len, ZSW(dlg->legs[callee_idx(dlg)].tag.s)); /*destroy linkers */ destroy_linkers(dlg->profile_links); dlg->profile_links = NULL; /* dialog timeout */ run_dlg_callbacks( DLGCB_EXPIRED, dlg, 0, DLG_DIR_NONE, 0); /* delete the dialog from DB */ if (should_remove_dlg_db()) remove_dialog_from_db(dlg); unref_dlg(dlg, unref + 1 /*timer list*/); if_update_stat( dlg_enable_stats, expired_dlgs, 1); if_update_stat( dlg_enable_stats, active_dlgs, -1); } else { unref_dlg(dlg, 1 /*just timer list*/); } return; }
void dlg_ping_routine(unsigned int ticks , void * attr) { struct dlg_ping_list *expired,*it,*curr; struct dlg_cell *dlg; expired = get_timeout_dlgs(); it = expired; while (it) { dlg = it->dlg; LM_DBG("dialog %p has expired\n",dlg); curr = it->next; shm_free(it); dlg->pl = 0; it = curr; /* no longer reffed in list */ unref_dlg(dlg,1); /* dlg is still reffed in TM callback. deletion from memory * will happen only on 408 timeout */ /* FIXME - maybe better not to send BYE both ways as we know for sure one * end in down . */ dlg_end_dlg(dlg,0); } /* ping_timer->first now contains all active dialogs */ it = ping_timer->first; while (it) { dlg = it->dlg; /* do not ping ended dialogs */ if (dlg->state != DLG_STATE_DELETED) { if (dlg->flags & DLG_FLAG_PING_CALLER) { ref_dlg(dlg,1); if (send_leg_msg(dlg,&options_str,callee_idx(dlg), DLG_CALLER_LEG,0,0,reply_from_caller,dlg,unref_dlg_cb) < 0) { LM_ERR("failed to ping caller\n"); unref_dlg(dlg,1); } } if (dlg->flags & DLG_FLAG_PING_CALLEE) { ref_dlg(dlg,1); if (send_leg_msg(dlg,&options_str,DLG_CALLER_LEG, callee_idx(dlg),0,0,reply_from_callee,dlg,unref_dlg_cb) < 0) { LM_ERR("failed to ping callee\n"); unref_dlg(dlg,1); } } } it = it->next; } }
/* referred to as a dialog."*/ struct dlg_cell* get_dlg( str *callid, str *ftag, str *ttag, unsigned int *dir, unsigned int *dst_leg) { struct dlg_cell *dlg; struct dlg_entry *d_entry; unsigned int h_entry; h_entry = dlg_hash(callid); d_entry = &(d_table->entries[h_entry]); dlg_lock( d_table, d_entry); LM_DBG("input ci=<%.*s>(%d), tt=<%.*s>(%d), ft=<%.*s>(%d)\n", callid->len,callid->s, callid->len, ftag->len, ftag->s, ftag->len, ttag->len, ttag->s, ttag->len); for( dlg = d_entry->first ; dlg ; dlg = dlg->next ) { /* Check callid / fromtag / totag */ #ifdef EXTRA_DEBUG LM_DBG("DLG (%p)(%d): ci=<%.*s>(%d), ft=<%.*s>(%d), tt=<%.*s>(%d)," "ct_er=%d, ct_ee=%d\n", dlg,dlg->state,dlg->callid.len,dlg->callid.s, dlg->callid.len, dlg->legs[DLG_CALLER_LEG].tag.len,dlg->legs[DLG_CALLER_LEG].tag.s, dlg->legs[DLG_CALLER_LEG].tag.len, dlg->legs[callee_idx(dlg)].tag.len,dlg->legs[callee_idx(dlg)].tag.s, dlg->legs[callee_idx(dlg)].tag.len, dlg->legs[DLG_CALLER_LEG].contact.len, dlg->legs[DLG_CALLER_LEG].contact.len); #endif if (match_dialog( dlg, callid, ftag, ttag, dir, dst_leg)==1) { if (dlg->state==DLG_STATE_DELETED) /* even if matched, skip the deleted dialogs as they may be a previous unsuccessful attempt of established call with the same callid and fromtag - like in auth/challenge case -bogdan */ continue; dlg->ref++; LM_DBG("ref dlg %p with 1 -> %d\n", dlg, dlg->ref); dlg_unlock( d_table, d_entry); LM_DBG("dialog callid='%.*s' found\n on entry %u, dir=%d\n", callid->len, callid->s,h_entry,*dir); return dlg; } } dlg_unlock( d_table, d_entry); LM_DBG("no dialog callid='%.*s' found\n", callid->len, callid->s); return 0; }
/** * replicates a local dialog update to all the destinations * specified with the 'replicate_dialogs' modparam */ void replicate_dialog_updated(struct dlg_cell *dlg) { struct replication_dest *d; static str module_name = str_init("dialog"); int callee_leg; str *vars, *profiles; if (bin_init(&module_name, REPLICATION_DLG_UPDATED) != 0) goto error; callee_leg = callee_idx(dlg); bin_push_str(&dlg->callid); bin_push_str(&dlg->legs[DLG_CALLER_LEG].tag); bin_push_str(&dlg->legs[callee_leg].tag); bin_push_str(&dlg->from_uri); bin_push_str(&dlg->to_uri); bin_push_int(dlg->h_id); bin_push_int(dlg->start_ts); bin_push_int(dlg->state); bin_push_str(&dlg->legs[DLG_CALLER_LEG].bind_addr->sock_str); if (dlg->legs[callee_leg].bind_addr) bin_push_str(&dlg->legs[callee_leg].bind_addr->sock_str); else bin_push_str(NULL); bin_push_str(&dlg->legs[DLG_CALLER_LEG].r_cseq); bin_push_str(&dlg->legs[callee_leg].r_cseq); bin_push_str(&dlg->legs[DLG_CALLER_LEG].route_set); bin_push_str(&dlg->legs[callee_leg].route_set); bin_push_str(&dlg->legs[DLG_CALLER_LEG].contact); bin_push_str(&dlg->legs[callee_leg].contact); bin_push_str(&dlg->legs[callee_leg].from_uri); bin_push_str(&dlg->legs[callee_leg].to_uri); /* XXX: on shutdown only? */ vars = write_dialog_vars(dlg->vals); profiles = write_dialog_profiles(dlg->profile_links); bin_push_str(vars); bin_push_str(profiles); bin_push_int(dlg->user_flags); bin_push_int(dlg->flags & ~(DLG_FLAG_NEW|DLG_FLAG_CHANGED|DLG_FLAG_VP_CHANGED)); bin_push_int((unsigned int)time(0) + dlg->tl.timeout - get_ticks()); bin_push_int(dlg->legs[DLG_CALLER_LEG].last_gen_cseq); bin_push_int(dlg->legs[callee_leg].last_gen_cseq); for (d = replication_dests; d; d = d->next) bin_send(&d->to); if_update_stat(dlg_enable_stats,update_sent,1); return; error: LM_ERR("Failed to replicate updated dialog\n"); }
int insert_ping_timer(struct dlg_cell* dlg) { struct dlg_ping_list *node; node = shm_malloc(sizeof(struct dlg_ping_list)); if (node == 0) { LM_ERR("no more shm mem\n"); return -1; } node->dlg = dlg; node->next = 0; node->prev = 0; lock_get( ping_timer->lock ); dlg->pl = node; if (ping_timer->first == 0) ping_timer->first = node; else { node->next = ping_timer->first; ping_timer->first->prev = node; ping_timer->first = node; } dlg->legs[DLG_CALLER_LEG].reply_received = 1; dlg->legs[callee_idx(dlg)].reply_received = 1; lock_release( ping_timer->lock); LM_DBG("Inserted dlg [%p] in ping timer list\n",dlg); return 0; }
/** * replicates a local dialog delete event to all the destinations * specified with the 'replicate_dialogs' modparam */ void replicate_dialog_deleted(struct dlg_cell *dlg) { int rc; bin_packet_t packet; if (bin_init(&packet, &dlg_repl_cap, REPLICATION_DLG_DELETED, BIN_VERSION, 1024) != 0) goto error; bin_push_str(&packet, &dlg->callid); bin_push_str(&packet, &dlg->legs[DLG_CALLER_LEG].tag); bin_push_str(&packet, &dlg->legs[callee_idx(dlg)].tag); rc = clusterer_api.send_all(&packet, dialog_repl_cluster); switch (rc) { case CLUSTERER_CURR_DISABLED: LM_INFO("Current node is disabled in cluster: %d\n", dialog_repl_cluster); goto error_free; case CLUSTERER_DEST_DOWN: LM_ERR("All destinations in cluster: %d are down or probing\n", dialog_repl_cluster); goto error_free; case CLUSTERER_SEND_ERR: LM_ERR("Error sending in cluster: %d\n", dialog_repl_cluster); goto error_free; } if_update_stat(dlg_enable_stats, delete_sent, 1); bin_free_packet(&packet); return; error_free: bin_free_packet(&packet); error: LM_ERR("Failed to replicate deleted dialog\n"); }
/* removes expired dlgs from main ping_timer list * and links them back into a new list */ struct dlg_ping_list* get_timeout_dlgs(void) { struct dlg_ping_list *ret = NULL,*it=NULL,*next=NULL; struct dlg_cell *current; int detached; lock_get(ping_timer->lock); for (it=ping_timer->first;it;it=next) { current = it->dlg; next = it->next; detached = 0; if (current->flags & DLG_FLAG_PING_CALLER) { dlg_lock_dlg(current); if (current->legs[DLG_CALLER_LEG].reply_received == 0) { dlg_unlock_dlg(current); detach_node_unsafe(it); detached=1; if (ret == NULL) ret = it; else { it->next = ret; ret = it; } } else dlg_unlock_dlg(current); } if (detached == 0) { if (current->flags & DLG_FLAG_PING_CALLEE) { dlg_lock_dlg(current); if (current->legs[callee_idx(current)].reply_received == 0) { dlg_unlock_dlg(current); detach_node_unsafe(it); if (ret == NULL) ret = it; else { it->next = ret; ret = it; } } else dlg_unlock_dlg(current); } } } lock_release(ping_timer->lock); return ret; }
/** * Small logging helper functions for next_state_dlg. * \param event logged event * \param dlg dialog data * \see next_state_dlg */ static inline void log_next_state_dlg(const int event, const struct dlg_cell *dlg) { LM_WARN("bogus event %d in state %d for dlg %p [%u:%u] with " "clid '%.*s' and tags '%.*s' '%.*s'\n", event, dlg->state, dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg_leg_print_info( dlg, DLG_CALLER_LEG, tag), dlg_leg_print_info( dlg, callee_idx(dlg), tag)); }
static inline void log_bogus_dst_leg(struct dlg_cell *dlg) { if (last_dst_leg>=dlg->legs_no[DLG_LEGS_USED]) LM_CRIT("bogus dst leg %d in state %d for dlg %p [%u:%u] with " "clid '%.*s' and tags '%.*s' '%.*s'. legs used %d\n", last_dst_leg,dlg->state, dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg_leg_print_info( dlg, DLG_CALLER_LEG, tag), dlg_leg_print_info( dlg, callee_idx(dlg), tag),dlg->legs_no[DLG_LEGS_USED]); }
/* sends BYE in both directions * returns 0 if both BYEs were successful */ int dlg_end_dlg(struct dlg_cell *dlg, str *extra_hdrs, int send_byes) { str str_hdr = {NULL,0}; struct cell* t; int i,res = 0; int callee; /* lookup_dlg has incremented the reference count !! */ if (send_byes && (dlg->state == DLG_STATE_UNCONFIRMED || dlg->state == DLG_STATE_EARLY)) { /* locate initial transaction */ LM_DBG("trying to find transaction with hash_index = %u and label = %u\n", dlg->initial_t_hash_index,dlg->initial_t_label); if (d_tmb.t_lookup_ident(&t,dlg->initial_t_hash_index,dlg->initial_t_label) < 0) { LM_ERR("Initial transaction does not exist any more\n"); return -1; } if (d_tmb.t_cancel_trans(t,NULL) < 0) { LM_ERR("Failed to send cancels\n"); d_tmb.unref_cell(t); return -1; } /* lookup_ident refs the transaction */ d_tmb.unref_cell(t); return 0; } if (send_byes && (build_extra_hdr(dlg, extra_hdrs, &str_hdr)) != 0){ LM_ERR("failed to create extra headers\n"); return -1; } callee = callee_idx(dlg); if (send_byes && send_leg_bye(dlg, DLG_CALLER_LEG, callee, &str_hdr)!=0) { res--; } if (send_byes && send_leg_bye(dlg, callee, DLG_CALLER_LEG, &str_hdr)!=0 ) { res--; } if (!send_byes) { dual_bye_event(dlg, NULL, 0); dual_bye_event(dlg, NULL, 0); } else for(i=res ; i<0 ; i++) dual_bye_event(dlg, NULL, 1); if (str_hdr.s) pkg_free(str_hdr.s); return res; }
int dlg_th_onroute(struct dlg_cell *dlg, struct sip_msg *req, int dir) { struct hdr_field *it; char* buf = req->buf; /* delete vias */ if(dlg_del_vias(req) < 0) { LM_ERR("Failed to remove via headers\n"); return -1; } /* delete record route */ for (it=req->record_route;it;it=it->sibling) { if (del_lump(req, it->name.s - buf, it->len,HDR_RECORDROUTE_T) == 0) { LM_ERR("del_lump failed\n"); return -1; } LM_DBG("Delete record route: [%.*s]\n", it->len, it->name.s); } /* add route headers */ fix_route_dialog(req, dlg); /* replace contact*/ if(dlg_replace_contact(req, dlg) < 0) { LM_ERR("Failed to replace contact\n"); return -1; } /* register tm callback for response in */ ref_dlg( dlg , 1); if ( d_tmb.register_tmcb( req, 0, TMCB_RESPONSE_FWDED, (dir==DLG_DIR_UPSTREAM)?dlg_th_down_onreply:dlg_th_up_onreply, (void*)dlg, unreference_dialog)<0 ) { LM_ERR("failed to register TMCB\n"); unref_dlg( dlg , 1); } if (dir == DLG_DIR_UPSTREAM) { /* destination leg is the caller - force the send socket * as the one the caller was inited from */ req->force_send_socket = dlg->legs[DLG_CALLER_LEG].bind_addr; LM_DBG("forcing send socket for req going to caller\n"); } else { /* destination leg is the callee - force the send socket * as the one the callee was inited from */ req->force_send_socket = dlg->legs[callee_idx(dlg)].bind_addr; LM_DBG("forcing send socket for req going to callee\n"); } return 0; }
void bin_push_dlg(bin_packet_t *packet, struct dlg_cell *dlg) { int callee_leg; str *vars, *profiles; callee_leg = callee_idx(dlg); bin_push_str(packet, &dlg->callid); bin_push_str(packet, &dlg->legs[DLG_CALLER_LEG].tag); bin_push_str(packet, &dlg->legs[callee_leg].tag); bin_push_str(packet, &dlg->from_uri); bin_push_str(packet, &dlg->to_uri); bin_push_int(packet, dlg->h_id); bin_push_int(packet, dlg->start_ts); bin_push_int(packet, dlg->state); bin_push_str(packet, &dlg->legs[DLG_CALLER_LEG].bind_addr->sock_str); if (dlg->legs[callee_leg].bind_addr) bin_push_str(packet, &dlg->legs[callee_leg].bind_addr->sock_str); else bin_push_str(packet, NULL); bin_push_str(packet, &dlg->legs[DLG_CALLER_LEG].r_cseq); bin_push_str(packet, &dlg->legs[callee_leg].r_cseq); bin_push_str(packet, &dlg->legs[DLG_CALLER_LEG].route_set); bin_push_str(packet, &dlg->legs[callee_leg].route_set); bin_push_str(packet, &dlg->legs[DLG_CALLER_LEG].contact); bin_push_str(packet, &dlg->legs[callee_leg].contact); bin_push_str(packet, &dlg->legs[callee_leg].from_uri); bin_push_str(packet, &dlg->legs[callee_leg].to_uri); /* give modules the chance to write values/profiles before replicating */ run_dlg_callbacks(DLGCB_WRITE_VP, dlg, NULL, DLG_DIR_NONE, NULL, 1); vars = write_dialog_vars(dlg->vals); profiles = write_dialog_profiles(dlg->profile_links); bin_push_str(packet, vars); bin_push_str(packet, profiles); bin_push_int(packet, dlg->user_flags); bin_push_int(packet, dlg->mod_flags); bin_push_int(packet, dlg->flags & ~(DLG_FLAG_NEW|DLG_FLAG_CHANGED|DLG_FLAG_VP_CHANGED|DLG_FLAG_FROM_DB)); bin_push_int(packet, (unsigned int)time(0) + dlg->tl.timeout - get_ticks()); bin_push_int(packet, dlg->legs[DLG_CALLER_LEG].last_gen_cseq); bin_push_int(packet, dlg->legs[callee_leg].last_gen_cseq); }
int dlg_th_onroute(struct dlg_cell *dlg, struct sip_msg *req, int dir) { struct hdr_field *it; char* buf = req->buf; int leg_id; if(dir == DLG_DIR_UPSTREAM) leg_id = callee_idx(dlg); else leg_id = DLG_CALLER_LEG; /* delete vias */ if(dlg_save_del_vias(req, &dlg->legs[leg_id]) < 0) { LM_ERR("Failed to save and remove via headers\n"); return -1; } /* delete record route */ for (it=req->record_route;it;it=it->sibling) { if (del_lump(req, it->name.s - buf, it->len,HDR_RECORDROUTE_T) == 0) { LM_ERR("del_lump failed \n"); return -1; } LM_DBG("Delete record route: [%.*s]\n", it->len, it->name.s); } /* add route headers */ fix_route_dialog(req, dlg); /* replace contact*/ if(dlg_replace_contact(req, dlg) < 0) { LM_ERR("Failed to replace contact\n"); return -1; } /* register tm callback for response in */ ref_dlg( dlg , 1); if ( d_tmb.register_tmcb( req, 0, TMCB_RESPONSE_FWDED, (dir==DLG_DIR_UPSTREAM)?dlg_th_down_onreply:dlg_th_up_onreply, (void*)dlg, unreference_dialog)<0 ) { LM_ERR("failed to register TMCB\n"); unref_dlg( dlg , 1); } return 0; }
/* Duplicate code for the sake of quickly knowing where the reply came from, * without any further checks */ void reply_from_callee(struct cell* t, int type, struct tmcb_params* ps) { struct sip_msg *rpl; int statuscode; struct dlg_cell *dlg; if(ps == NULL || ps->rpl == NULL) { LM_ERR("Wrong tmcb params\n"); return; } if( ps->param== NULL ) { LM_ERR("Null callback parameter\n"); return; } rpl = ps->rpl; statuscode = ps->code; dlg = *(ps->param); LM_DBG("Status Code received = [%d]\n", statuscode); if (rpl == FAKED_REPLY || statuscode == 408) { /* timeout occured, nothing else to do now * next time timer fires, it will detect ping reply was not received */ LM_INFO("terminating dialog ( due to timeout ) " "with callid = [%.*s] \n",dlg->callid.len,dlg->callid.s); return; } if (statuscode == 481) { /* call/transaction does not exist * terminate the dialog */ LM_INFO("terminating dialog ( due to 481 ) " "with callid = [%.*s] \n",dlg->callid.len,dlg->callid.s); return; } dlg_lock_dlg(dlg); dlg->legs[callee_idx(dlg)].reply_received = 1; dlg_unlock_dlg(dlg); }
/** * replicates a local dialog delete event to all the destinations * specified with the 'replicate_dialogs' modparam */ void replicate_dialog_deleted(struct dlg_cell *dlg) { struct replication_dest *d; static str module_name = str_init("dialog"); if (bin_init(&module_name, REPLICATION_DLG_DELETED) != 0) goto error; bin_push_str(&dlg->callid); bin_push_str(&dlg->legs[DLG_CALLER_LEG].tag); bin_push_str(&dlg->legs[callee_idx(dlg)].tag); for (d = replication_dests; d; d = d->next) bin_send(&d->to); if_update_stat(dlg_enable_stats,delete_sent,1); return; error: LM_ERR("Failed to replicate deleted dialog\n"); }
/** * replicates a local dialog delete event to all the destinations * specified with the 'replicate_dialogs' modparam */ void replicate_dialog_deleted(struct dlg_cell *dlg) { static str module_name = str_init("dialog"); if (bin_init(&module_name, REPLICATION_DLG_DELETED, BIN_VERSION) != 0) goto error; bin_push_int(clusterer_api.get_my_id()); bin_push_str(&dlg->callid); bin_push_str(&dlg->legs[DLG_CALLER_LEG].tag); bin_push_str(&dlg->legs[callee_idx(dlg)].tag); if (clusterer_api.send_to(dialog_replicate_cluster, PROTO_BIN) < 0) { goto error; } return; error: LM_ERR("Failed to replicate deleted dialog\n"); }
/** * replicates a local dialog delete event to all the destinations * specified with the 'replicate_dialogs' modparam */ void replicate_dialog_deleted(struct dlg_cell *dlg) { struct replication_dest *d; static str module_name = str_init("dialog"); str send_buffer; if (bin_init(&module_name, REPLICATION_DLG_DELETED, BIN_VERSION) != 0) goto error; bin_push_str(&dlg->callid); bin_push_str(&dlg->legs[DLG_CALLER_LEG].tag); bin_push_str(&dlg->legs[callee_idx(dlg)].tag); bin_get_buffer(&send_buffer); for (d = replication_dests; d; d = d->next) msg_send(0,PROTO_BIN,&d->to,0,send_buffer.s,send_buffer.len,0); if_update_stat(dlg_enable_stats,delete_sent,1); return; error: LM_ERR("Failed to replicate deleted dialog\n"); }
/** * replicates the remote update of an ongoing dialog locally * by reading the relevant information using the Binary Packet Interface */ int dlg_replicated_update(bin_packet_t *packet) { struct dlg_cell *dlg; str call_id, from_tag, to_tag, from_uri, to_uri, vars, profiles; unsigned int dir, dst_leg; int timeout, h_entry; str st; struct dlg_entry *d_entry; int rcv_flags, save_new_flag; bin_pop_str(packet, &call_id); bin_pop_str(packet, &from_tag); bin_pop_str(packet, &to_tag); bin_pop_str(packet, &from_uri); bin_pop_str(packet, &to_uri); LM_DBG("replicated update for ['%.*s' '%.*s' '%.*s' '%.*s' '%.*s']\n", call_id.len, call_id.s, from_tag.len, from_tag.s, to_tag.len, to_tag.s, from_uri.len, from_uri.s, to_uri.len, to_uri.s); dlg = get_dlg(&call_id, &from_tag, &to_tag, &dir, &dst_leg); h_entry = dlg_hash(&call_id); d_entry = &d_table->entries[h_entry]; dlg_lock(d_table, d_entry); if (!dlg) { LM_DBG("dialog not found, building new\n"); dlg = build_new_dlg(&call_id, &from_uri, &to_uri, &from_tag); if (!dlg) { LM_ERR("Failed to create replicated dialog!\n"); goto error; } return dlg_replicated_create(packet ,dlg, &from_tag, &to_tag, 0); } bin_skip_int(packet, 2); bin_pop_int(packet, &dlg->state); bin_skip_str(packet, 2); bin_pop_str(packet, &st); if (dlg_update_cseq(dlg, DLG_CALLER_LEG, &st, 0) != 0) { LM_ERR("failed to update caller cseq\n"); goto error; } bin_pop_str(packet, &st); if (dlg_update_cseq(dlg, callee_idx(dlg), &st, 0) != 0) { LM_ERR("failed to update callee cseq\n"); goto error; } bin_skip_str(packet, 6); bin_pop_str(packet, &vars); bin_pop_str(packet, &profiles); bin_pop_int(packet, &dlg->user_flags); bin_pop_int(packet, &dlg->mod_flags); bin_pop_int(packet, &rcv_flags); /* make sure an update received immediately after a create can't * incorrectly erase the DLG_FLAG_NEW before locally writing to DB */ save_new_flag = dlg->flags & DLG_FLAG_NEW; dlg->flags = rcv_flags; dlg->flags |= ((save_new_flag ? DLG_FLAG_NEW : 0) | DLG_FLAG_CHANGED); bin_pop_int(packet, &timeout); bin_skip_int(packet, 2); timeout -= time(0); LM_DBG("Received updated timeout of %d for dialog %.*s\n", timeout, call_id.len, call_id.s); if (dlg->lifetime != timeout) { dlg->lifetime = timeout; switch (update_dlg_timer(&dlg->tl, dlg->lifetime) ) { case -1: LM_ERR("failed to update dialog lifetime!\n"); /* continue */ case 0: /* timeout value was updated */ break; case 1: /* dlg inserted in timer list with new expire (reference it)*/ ref_dlg(dlg,1); } } unref_dlg_unsafe(dlg, 1, d_entry); if (vars.s && vars.len != 0) read_dialog_vars(vars.s, vars.len, dlg); dlg->flags |= DLG_FLAG_VP_CHANGED; dlg_unlock(d_table, d_entry); if (profiles.s && profiles.len != 0) read_dialog_profiles(profiles.s, profiles.len, dlg, 1, 1); return 0; error: dlg_unlock(d_table, d_entry); return -1; }
/** * replicates the remote update of an ongoing dialog locally * by reading the relevant information using the Binary Packet Interface */ int dlg_replicated_update(void) { struct dlg_cell *dlg; str call_id, from_tag, to_tag, from_uri, to_uri, vars, profiles; unsigned int dir, dst_leg; int timeout, h_entry; str st; struct dlg_entry *d_entry; bin_pop_str(&call_id); bin_pop_str(&from_tag); bin_pop_str(&to_tag); bin_pop_str(&from_uri); bin_pop_str(&to_uri); LM_DBG("replicated update for ['%.*s' '%.*s' '%.*s' '%.*s' '%.*s']\n", call_id.len, call_id.s, from_tag.len, from_tag.s, to_tag.len, to_tag.s, from_uri.len, from_uri.s, to_uri.len, to_uri.s); dlg = get_dlg(&call_id, &from_tag, &to_tag, &dir, &dst_leg); h_entry = dlg_hash(&call_id); d_entry = &d_table->entries[h_entry]; dlg_lock(d_table, d_entry); if (!dlg) { /* TODO: change to LM_DBG */ LM_ERR("dialog not found, building new\n"); dlg = build_new_dlg(&call_id, &from_uri, &to_uri, &from_tag); if (!dlg) { LM_ERR("Failed to create replicated dialog!\n"); goto error; } return dlg_replicated_create(dlg, &from_tag, &to_tag, 0); } bin_skip_int(2); bin_pop_int(&dlg->state); bin_skip_str(2); bin_pop_str(&st); if (dlg_update_cseq(dlg, DLG_CALLER_LEG, &st, 0) != 0) { LM_ERR("failed to update caller cseq\n"); goto error; } bin_pop_str(&st); if (dlg_update_cseq(dlg, callee_idx(dlg), &st, 0) != 0) { LM_ERR("failed to update callee cseq\n"); goto error; } bin_skip_str(6); bin_pop_str(&vars); bin_pop_str(&profiles); bin_pop_int(&dlg->user_flags); bin_pop_int(&dlg->flags); bin_pop_int(&timeout); bin_skip_int(2); timeout -= time(0); LM_DBG("Received updated timeout of %d for dialog %.*s\n",timeout,call_id.len,call_id.s); if (dlg->lifetime != timeout) { dlg->lifetime = timeout; if (update_dlg_timer(&dlg->tl, dlg->lifetime) == -1) LM_ERR("failed to update dialog lifetime!\n"); } unref_dlg_unsafe(dlg, 1, d_entry); if (vars.s && vars.len != 0) read_dialog_vars(vars.s, vars.len, dlg); dlg_unlock(d_table, d_entry); if (profiles.s && profiles.len != 0) read_dialog_profiles(profiles.s, profiles.len, dlg, 1, 1); return 0; error: dlg_unlock(d_table, d_entry); return -1; }
static int load_dialog_info_from_db(int dlg_hash_size) { db_res_t * res; db_val_t * values; db_row_t * rows; int i, nr_rows; struct dlg_cell *dlg; str callid, from_uri, to_uri, from_tag, to_tag; str cseq1,cseq2,contact1,contact2,rroute1,rroute2,mangled_fu,mangled_tu; unsigned int next_id; int no_rows = 10; struct socket_info *caller_sock,*callee_sock; res = 0; if((nr_rows = select_entire_dialog_table(&res,&no_rows)) < 0) goto end; nr_rows = RES_ROW_N(res); do { LM_DBG("loading information from database for %i dialogs\n", nr_rows); rows = RES_ROWS(res); /* for every row---dialog */ for(i=0; i<nr_rows; i++){ values = ROW_VALUES(rows + i); if (VAL_NULL(values) || VAL_NULL(values+1)) { LM_ERR("columns %.*s or/and %.*s cannot be null -> skipping\n", h_entry_column.len, h_entry_column.s, h_id_column.len, h_id_column.s); continue; } if (VAL_NULL(values+7) || VAL_NULL(values+8)) { LM_ERR("columns %.*s or/and %.*s cannot be null -> skipping\n", start_time_column.len, start_time_column.s, state_column.len, state_column.s); continue; } if ( VAL_INT(values+8) == DLG_STATE_DELETED ) { LM_DBG("dialog already terminated -> skipping\n"); continue; } caller_sock = create_socket_info(values, 16); callee_sock = create_socket_info(values, 17); if (caller_sock == NULL || callee_sock == NULL) { LM_ERR("Dialog in DB doesn't match any listening sockets"); continue; } /*restore the dialog info*/ GET_STR_VALUE(callid, values, 2, 1, 0); GET_STR_VALUE(from_uri, values, 3, 1, 0); GET_STR_VALUE(from_tag, values, 4, 1, 0); GET_STR_VALUE(to_uri, values, 5, 1, 0); if((dlg=build_new_dlg(&callid, &from_uri, &to_uri, &from_tag))==0){ LM_ERR("failed to build new dialog\n"); goto error; } if(dlg->h_entry != VAL_INT(values)){ LM_ERR("inconsistent hash data in the dialog database: " "you may have restarted opensips using a different " "hash_size: please erase %.*s database and restart\n", dialog_table_name.len, dialog_table_name.s); shm_free(dlg); goto error; } /*link the dialog*/ link_dlg(dlg, 0); dlg->h_id = VAL_INT(values+1); next_id = d_table->entries[dlg->h_entry].next_id; d_table->entries[dlg->h_entry].next_id = (next_id < dlg->h_id) ? (dlg->h_id+1) : next_id; GET_STR_VALUE(to_tag, values, 6, 1, 1); dlg->start_ts = VAL_INT(values+7); dlg->state = VAL_INT(values+8); if (dlg->state==DLG_STATE_CONFIRMED_NA || dlg->state==DLG_STATE_CONFIRMED) { active_dlgs_cnt++; } else if (dlg->state==DLG_STATE_EARLY) { early_dlgs_cnt++; } GET_STR_VALUE(cseq1, values, 10 , 1, 1); GET_STR_VALUE(cseq2, values, 11 , 1, 1); GET_STR_VALUE(rroute1, values, 12, 0, 0); GET_STR_VALUE(rroute2, values, 13, 0, 0); GET_STR_VALUE(contact1, values, 14, 0, 1); GET_STR_VALUE(contact2, values, 15, 0, 1); GET_STR_VALUE(mangled_fu, values, 24,0,1); GET_STR_VALUE(mangled_tu, values, 25,0,1); /* add the 2 legs */ if ( (dlg_add_leg_info( dlg, &from_tag, &rroute1, &contact1, &cseq1, caller_sock,0,0)!=0) || (dlg_add_leg_info( dlg, &to_tag, &rroute2, &contact2, &cseq2, callee_sock,&mangled_fu,&mangled_tu)!=0) ) { LM_ERR("dlg_set_leg_info failed\n"); /* destroy the dialog */ unref_dlg(dlg,1); continue; } dlg->legs_no[DLG_LEG_200OK] = DLG_FIRST_CALLEE_LEG; /* script variables */ if (!VAL_NULL(values+18)) read_dialog_vars( VAL_STR(values+18).s, VAL_STR(values+18).len, dlg); /* profiles */ if (!VAL_NULL(values+19)) read_dialog_profiles( VAL_STR(values+19).s, strlen(VAL_STR(values+19).s), dlg,0); /* script flags */ if (!VAL_NULL(values+20)) { dlg->user_flags = VAL_INT(values+20); } /* top hiding */ dlg->flags = VAL_INT(values+23); if (dlg_db_mode==DB_MODE_SHUTDOWN) dlg->flags |= DLG_FLAG_NEW; /* calculcate timeout */ dlg->tl.timeout = (unsigned int)(VAL_INT(values+9)) + get_ticks(); if (dlg->tl.timeout<=(unsigned int)time(0)) dlg->tl.timeout = 0; else dlg->tl.timeout -= (unsigned int)time(0); /* restore the timer values */ if (0 != insert_dlg_timer( &(dlg->tl), (int)dlg->tl.timeout )) { LM_CRIT("Unable to insert dlg %p [%u:%u] " "with clid '%.*s' and tags '%.*s' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg->legs[DLG_CALLER_LEG].tag.len, dlg->legs[DLG_CALLER_LEG].tag.s, dlg->legs[callee_idx(dlg)].tag.len, ZSW(dlg->legs[callee_idx(dlg)].tag.s)); /* destroy the dialog */ unref_dlg(dlg,1); continue; } /* reference the dialog as kept in the timer list */ ref_dlg(dlg,1); LM_DBG("current dialog timeout is %u\n", dlg->tl.timeout); dlg->lifetime = 0; dlg->legs[DLG_CALLER_LEG].last_gen_cseq = (unsigned int)(VAL_INT(values+21)); dlg->legs[callee_idx(dlg)].last_gen_cseq = (unsigned int)(VAL_INT(values+22)); if (dlg->flags & DLG_FLAG_PING_CALLER || dlg->flags & DLG_FLAG_PING_CALLEE) { if (0 != insert_ping_timer(dlg)) LM_CRIT("Unable to insert dlg %p into ping timer\n",dlg); else { /* reference dialog as kept in ping timer list */ ref_dlg(dlg,1); } } next_dialog: ; } /* any more data to be fetched ?*/ if (DB_CAPABILITY(dialog_dbf, DB_CAP_FETCH)) { if (dialog_dbf.fetch_result( dialog_db_handle, &res,no_rows) < 0) { LM_ERR("fetching more rows failed\n"); goto error; } nr_rows = RES_ROW_N(res); } else { nr_rows = 0; } }while (nr_rows>0); end: dialog_dbf.free_result(dialog_db_handle, res); return 0; error: dialog_dbf.free_result(dialog_db_handle, res); return -1; }
static int sync_dlg_db_mem(void) { db_res_t * res; db_val_t * values; db_row_t * rows; struct dlg_entry *d_entry; struct dlg_cell *it,*known_dlg,*dlg=NULL; int i, nr_rows,callee_leg_idx,next_id,db_timeout; int no_rows = 10; unsigned int db_caller_cseq,db_callee_cseq,dlg_caller_cseq,dlg_callee_cseq; struct socket_info *caller_sock,*callee_sock; str callid, from_uri, to_uri, from_tag, to_tag; str cseq1,cseq2,contact1,contact2,rroute1,rroute2,mangled_fu,mangled_tu; res = 0; if((nr_rows = select_entire_dialog_table(&res,&no_rows)) < 0) goto error; nr_rows = RES_ROW_N(res); do { LM_DBG("loading information from database for %i dialogs\n", nr_rows); rows = RES_ROWS(res); /* for every row---dialog */ for(i=0; i<nr_rows; i++){ values = ROW_VALUES(rows + i); if (VAL_NULL(values) || VAL_NULL(values+1)) { LM_ERR("columns %.*s or/and %.*s cannot be null -> skipping\n", h_entry_column.len, h_entry_column.s, h_id_column.len, h_id_column.s); continue; } if (VAL_NULL(values+7) || VAL_NULL(values+8)) { LM_ERR("columns %.*s or/and %.*s cannot be null -> skipping\n", start_time_column.len, start_time_column.s, state_column.len, state_column.s); continue; } if ( VAL_INT(values+8) == DLG_STATE_DELETED ) { LM_DBG("dialog already terminated -> skipping\n"); continue; } /*restore the dialog info*/ GET_STR_VALUE(callid, values, 2, 1, 0); GET_STR_VALUE(from_tag, values, 4, 1, 0); GET_STR_VALUE(to_tag, values, 6, 1, 1); /* TODO - check about hash resize ? maybe hash was lowered & we overflow the hash */ known_dlg = 0; d_entry = &(d_table->entries[VAL_INT(values)]); for (it=d_entry->first;it;it=it->next) if (it->callid.len == callid.len && it->legs[DLG_CALLER_LEG].tag.len == from_tag.len && memcmp(it->callid.s,callid.s,callid.len)==0 && memcmp(it->legs[DLG_CALLER_LEG].tag.s,from_tag.s,from_tag.len)==0) { /* callid & ftag match */ callee_leg_idx = callee_idx(it); if (it->legs[callee_leg_idx].tag.len == to_tag.len && memcmp(it->legs[callee_leg_idx].tag.s,to_tag.s,to_tag.len)==0) { /* full dlg match */ known_dlg = it; break; } } if (known_dlg == 0) { LM_DBG("First seen dialog - load all stuff - callid = [%.*s]\n",callid.len,callid.s); GET_STR_VALUE(from_uri, values, 3, 1, 0); GET_STR_VALUE(to_uri, values, 5, 1, 0); caller_sock = create_socket_info(values, 16); callee_sock = create_socket_info(values, 17); if (caller_sock == NULL || callee_sock == NULL) { LM_ERR("Dialog in DB doesn't match any listening sockets"); continue; } /* first time we see this dialog - build it from scratch */ if((dlg=build_new_dlg(&callid, &from_uri, &to_uri, &from_tag))==0){ LM_ERR("failed to build new dialog\n"); goto error; } if(dlg->h_entry != VAL_INT(values)){ LM_ERR("inconsistent hash data in the dialog database: " "you may have restarted opensips using a different " "hash_size: please erase %.*s database and restart\n", dialog_table_name.len, dialog_table_name.s); shm_free(dlg); goto error; } /*link the dialog*/ link_dlg(dlg, 0); dlg->h_id = VAL_INT(values+1); next_id = d_table->entries[dlg->h_entry].next_id; d_table->entries[dlg->h_entry].next_id = (next_id < dlg->h_id) ? (dlg->h_id+1) : next_id; dlg->start_ts = VAL_INT(values+7); dlg->state = VAL_INT(values+8); if (dlg->state==DLG_STATE_CONFIRMED_NA || dlg->state==DLG_STATE_CONFIRMED) { if_update_stat(dlg_enable_stats, active_dlgs, 1); } else if (dlg->state==DLG_STATE_EARLY) { if_update_stat(dlg_enable_stats, early_dlgs, 1); } GET_STR_VALUE(cseq1, values, 10 , 1, 1); GET_STR_VALUE(cseq2, values, 11 , 1, 1); GET_STR_VALUE(rroute1, values, 12, 0, 0); GET_STR_VALUE(rroute2, values, 13, 0, 0); GET_STR_VALUE(contact1, values, 14, 0, 1); GET_STR_VALUE(contact2, values, 15, 0, 1); GET_STR_VALUE(mangled_fu, values, 24,0,1); GET_STR_VALUE(mangled_tu, values, 25,0,1); /* add the 2 legs */ if ( (dlg_add_leg_info( dlg, &from_tag, &rroute1, &contact1, &cseq1, caller_sock,0,0)!=0) || (dlg_add_leg_info( dlg, &to_tag, &rroute2, &contact2, &cseq2, callee_sock,&mangled_fu,&mangled_tu)!=0) ) { LM_ERR("dlg_set_leg_info failed\n"); /* destroy the dialog */ unref_dlg(dlg,1); continue; } dlg->legs_no[DLG_LEG_200OK] = DLG_FIRST_CALLEE_LEG; /* script variables */ if (!VAL_NULL(values+18)) read_dialog_vars( VAL_STR(values+18).s, VAL_STR(values+18).len, dlg); /* profiles */ if (!VAL_NULL(values+19)) read_dialog_profiles( VAL_STR(values+19).s, strlen(VAL_STR(values+19).s), dlg,0); /* script flags */ if (!VAL_NULL(values+20)) { dlg->user_flags = VAL_INT(values+20); } /* top hiding */ dlg->flags = VAL_INT(values+23); if (dlg_db_mode==DB_MODE_SHUTDOWN) dlg->flags |= DLG_FLAG_NEW; /* calculcate timeout */ dlg->tl.timeout = (unsigned int)(VAL_INT(values+9)) + get_ticks(); if (dlg->tl.timeout<=(unsigned int)time(0)) dlg->tl.timeout = 0; else dlg->tl.timeout -= (unsigned int)time(0); /* restore the timer values */ if (0 != insert_dlg_timer( &(dlg->tl), (int)dlg->tl.timeout )) { LM_CRIT("Unable to insert dlg %p [%u:%u] " "with clid '%.*s' and tags '%.*s' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg->legs[DLG_CALLER_LEG].tag.len, dlg->legs[DLG_CALLER_LEG].tag.s, dlg->legs[callee_idx(dlg)].tag.len, ZSW(dlg->legs[callee_idx(dlg)].tag.s)); /* destroy the dialog */ unref_dlg(dlg,1); continue; } /* reference the dialog as kept in the timer list */ ref_dlg(dlg,1); LM_DBG("current dialog timeout is %u\n", dlg->tl.timeout); dlg->lifetime = 0; dlg->legs[DLG_CALLER_LEG].last_gen_cseq = (unsigned int)(VAL_INT(values+21)); dlg->legs[callee_idx(dlg)].last_gen_cseq = (unsigned int)(VAL_INT(values+22)); if (dlg->flags & DLG_FLAG_PING_CALLER || dlg->flags & DLG_FLAG_PING_CALLEE) { if (0 != insert_ping_timer(dlg)) LM_CRIT("Unable to insert dlg %p into ping timer\n",dlg); else { /* reference dialog as kept in ping timer list */ ref_dlg(dlg,1); } } } else { /* we already saw this dialog before * check which is the newer version */ if (known_dlg->state > VAL_INT(values+8)) { LM_DBG("mem has a newer state - ignore \n"); /* we know a newer version compared to the DB * ignore it */ goto next_dialog; } else if (known_dlg->state == VAL_INT(values+8)) { LM_DBG("mem has same state as DB \n"); /* same state :-( no way to tell which is newer */ /* play nice and store longest timeout, although not always correct*/ db_timeout = (unsigned int)(VAL_INT(values+9)) + get_ticks(); if (db_timeout<=(unsigned int)time(0)) db_timeout = 0; else db_timeout -= (unsigned int)time(0); if (known_dlg->tl.timeout < db_timeout) known_dlg->tl.timeout = db_timeout; /* check with is newer cseq for caller leg */ if (!VAL_NULL(values+10)) { cseq1.s = VAL_STR(values+10).s; cseq1.len = strlen(cseq1.s); str2int(&cseq1,&db_caller_cseq); str2int(&known_dlg->legs[DLG_CALLER_LEG].r_cseq,&dlg_caller_cseq); /* Is DB cseq newer ? */ if (db_caller_cseq > dlg_caller_cseq) { if (known_dlg->legs[DLG_CALLER_LEG].r_cseq.len < cseq1.len) { known_dlg->legs[DLG_CALLER_LEG].r_cseq.s = shm_realloc(known_dlg->legs[DLG_CALLER_LEG].r_cseq.s,cseq1.len); if (!known_dlg->legs[DLG_CALLER_LEG].r_cseq.s) { LM_ERR("no more shm\n"); goto next_dialog; } } memcpy(known_dlg->legs[DLG_CALLER_LEG].r_cseq.s,cseq1.s,cseq1.len); known_dlg->legs[DLG_CALLER_LEG].r_cseq.len = cseq1.len; } } else { /* DB has a null cseq - just keep * what we have so far */ ; } /* check with is newer cseq for caller leg */ if (!VAL_NULL(values+11)) { cseq2.s = VAL_STR(values+11).s; cseq2.len = strlen(cseq2.s); callee_leg_idx = callee_idx(known_dlg); str2int(&cseq2,&db_callee_cseq); str2int(&known_dlg->legs[callee_leg_idx].r_cseq,&dlg_callee_cseq); /* Is DB cseq newer ? */ if (db_callee_cseq > dlg_callee_cseq) { if (known_dlg->legs[callee_leg_idx].r_cseq.len < cseq2.len) { known_dlg->legs[callee_leg_idx].r_cseq.s = shm_realloc(known_dlg->legs[callee_leg_idx].r_cseq.s,cseq2.len); if (!known_dlg->legs[callee_leg_idx].r_cseq.s) { LM_ERR("no more shm\n"); goto next_dialog; } } memcpy(known_dlg->legs[callee_leg_idx].r_cseq.s,cseq2.s,cseq2.len); known_dlg->legs[callee_leg_idx].r_cseq.len = cseq2.len; } } else { /* DB has a null cseq - just keep * what we have so far */ ; } /* update ping cseqs, whichever is newer */ if (known_dlg->legs[DLG_CALLER_LEG].last_gen_cseq < (unsigned int)(VAL_INT(values+21))) known_dlg->legs[DLG_CALLER_LEG].last_gen_cseq = (unsigned int)(VAL_INT(values+21)); if (known_dlg->legs[callee_idx(known_dlg)].last_gen_cseq < (unsigned int)(VAL_INT(values+22))) known_dlg->legs[callee_idx(known_dlg)].last_gen_cseq = (unsigned int)(VAL_INT(values+22)); /* update script variables * if already found, delete the old ones * and replace with new one */ if (!VAL_NULL(values+18)) read_dialog_vars( VAL_STR(values+18).s, VAL_STR(values+18).len, known_dlg); /* skip flags - keep what we have - anyway can't tell which is new */ /* profiles - do not insert into a profile * is dlg is already in that profile*/ if (!VAL_NULL(values+19)) read_dialog_profiles( VAL_STR(values+19).s, strlen(VAL_STR(values+19).s), known_dlg,1); } else { /* DB has newer state, just update fields from DB */ LM_DBG("DB has newer state \n"); /* set new state */ known_dlg->state = VAL_INT(values+8); /* update timeout */ known_dlg->tl.timeout = (unsigned int)(VAL_INT(values+9)) + get_ticks(); if (known_dlg->tl.timeout<=(unsigned int)time(0)) known_dlg->tl.timeout = 0; else known_dlg->tl.timeout -= (unsigned int)time(0); /* update cseqs */ if (!VAL_NULL(values+10)) { cseq1.s = VAL_STR(values+10).s; cseq1.len = strlen(cseq1.s); if (known_dlg->legs[DLG_CALLER_LEG].r_cseq.len < cseq1.len) { known_dlg->legs[DLG_CALLER_LEG].r_cseq.s = shm_realloc(known_dlg->legs[DLG_CALLER_LEG].r_cseq.s,cseq1.len); if (!known_dlg->legs[DLG_CALLER_LEG].r_cseq.s) { LM_ERR("no more shm\n"); goto next_dialog; } } memcpy(known_dlg->legs[DLG_CALLER_LEG].r_cseq.s,cseq1.s,cseq1.len); known_dlg->legs[DLG_CALLER_LEG].r_cseq.len = cseq1.len; } if (!VAL_NULL(values+11)) { cseq2.s = VAL_STR(values+11).s; cseq2.len = strlen(cseq1.s); callee_leg_idx = callee_idx(known_dlg); if (known_dlg->legs[callee_leg_idx].r_cseq.len < cseq2.len) { known_dlg->legs[callee_leg_idx].r_cseq.s = shm_realloc(known_dlg->legs[callee_leg_idx].r_cseq.s,cseq2.len); if (!known_dlg->legs[callee_leg_idx].r_cseq.s) { LM_ERR("no more shm\n"); goto next_dialog; } } memcpy(known_dlg->legs[callee_leg_idx].r_cseq.s,cseq2.s,cseq2.len); known_dlg->legs[callee_leg_idx].r_cseq.len = cseq2.len; } /* update ping cseqs */ known_dlg->legs[DLG_CALLER_LEG].last_gen_cseq = (unsigned int)(VAL_INT(values+21)); known_dlg->legs[callee_idx(known_dlg)].last_gen_cseq = (unsigned int)(VAL_INT(values+22)); /* update flags */ known_dlg->flags = VAL_INT(values+23); if (dlg_db_mode==DB_MODE_SHUTDOWN) known_dlg->flags |= DLG_FLAG_NEW; /* update script variables * if already found, delete the old one * and replace with new one */ if (!VAL_NULL(values+18)) read_dialog_vars( VAL_STR(values+18).s, VAL_STR(values+18).len, known_dlg); /* profiles - do not insert into a profile * is dlg is already in that profile*/ if (!VAL_NULL(values+19)) read_dialog_profiles( VAL_STR(values+19).s, strlen(VAL_STR(values+19).s), known_dlg,1); } } next_dialog: ; } /* any more data to be fetched ?*/ if (DB_CAPABILITY(dialog_dbf, DB_CAP_FETCH)) { if (dialog_dbf.fetch_result( dialog_db_handle, &res,no_rows) < 0) { LM_ERR("fetching more rows failed\n"); goto error; } nr_rows = RES_ROW_N(res); } else { nr_rows = 0; } }while (nr_rows>0); return 0; error: return -1; }
int update_dialog_dbinfo(struct dlg_cell * cell) { static db_ps_t my_ps_insert = NULL; static db_ps_t my_ps_update = NULL; static db_ps_t my_ps_update_vp = NULL; struct dlg_entry entry; db_val_t values[DIALOG_TABLE_TOTAL_COL_NO]; int callee_leg; db_key_t insert_keys[DIALOG_TABLE_TOTAL_COL_NO] = { &h_entry_column, &h_id_column, &call_id_column, &from_uri_column, &from_tag_column, &to_uri_column, &to_tag_column, &from_sock_column, &to_sock_column, &start_time_column, &mangled_fu_column, &mangled_tu_column, &state_column, &timeout_column, &from_cseq_column, &to_cseq_column, &from_ping_cseq_column, &to_ping_cseq_column,&flags_column, &vars_column, &profiles_column, &sflags_column, &from_route_column, &to_route_column, &from_contact_column,&to_contact_column}; if(use_dialog_table()!=0) return -1; callee_leg= callee_idx(cell); if((cell->flags & DLG_FLAG_NEW) != 0){ /* save all the current dialogs information*/ VAL_TYPE(values) = VAL_TYPE(values+1) = VAL_TYPE(values+9) = VAL_TYPE(values+12) = VAL_TYPE(values+13) = VAL_TYPE(values+16) = VAL_TYPE(values+17) = VAL_TYPE(values+18) = VAL_TYPE(values+21) = DB_INT; VAL_TYPE(values+2) = VAL_TYPE(values+3) = VAL_TYPE(values+4) = VAL_TYPE(values+5) = VAL_TYPE(values+6) = VAL_TYPE(values+7) = VAL_TYPE(values+8) = VAL_TYPE(values+10) = VAL_TYPE(values+11) = VAL_TYPE(values+14) = VAL_TYPE(values+15) = VAL_TYPE(values+19) = VAL_TYPE(values+20) = VAL_TYPE(values+22) = VAL_TYPE(values+23) = VAL_TYPE(values+24) = VAL_TYPE(values+25) = DB_STR; /* lock the entry */ entry = (d_table->entries)[cell->h_entry]; dlg_lock( d_table, &entry); SET_INT_VALUE(values, cell->h_entry); SET_INT_VALUE(values+1, cell->h_id); SET_STR_VALUE(values+2, cell->callid); SET_STR_VALUE(values+3, cell->from_uri); SET_STR_VALUE(values+4, cell->legs[DLG_CALLER_LEG].tag); SET_STR_VALUE(values+5, cell->to_uri); SET_STR_VALUE(values+6, cell->legs[callee_leg].tag); SET_STR_VALUE(values+7, cell->legs[DLG_CALLER_LEG].bind_addr->sock_str); if (cell->legs[callee_leg].bind_addr) { SET_STR_VALUE(values+8, cell->legs[callee_leg].bind_addr->sock_str); } else { VAL_NULL(values+8) = 1; } SET_INT_VALUE(values+9, cell->start_ts); SET_STR_VALUE(values+10,cell->legs[callee_leg].from_uri); SET_STR_VALUE(values+11,cell->legs[callee_leg].to_uri); SET_INT_VALUE(values+12, cell->state); SET_INT_VALUE(values+13, (unsigned int)( (unsigned int)time(0) + cell->tl.timeout - get_ticks()) ); SET_STR_VALUE(values+14, cell->legs[DLG_CALLER_LEG].r_cseq); SET_STR_VALUE(values+15, cell->legs[callee_leg].r_cseq); SET_INT_VALUE(values+16,cell->legs[DLG_CALLER_LEG].last_gen_cseq); SET_INT_VALUE(values+17,cell->legs[callee_leg].last_gen_cseq); SET_INT_VALUE(values+18, cell->flags & ~(DLG_FLAG_NEW|DLG_FLAG_CHANGED|DLG_FLAG_VP_CHANGED)); set_final_update_cols(values+19, cell, 0); SET_STR_VALUE(values+22, cell->legs[DLG_CALLER_LEG].route_set); SET_STR_VALUE(values+23, cell->legs[callee_leg].route_set); SET_STR_VALUE(values+24, cell->legs[DLG_CALLER_LEG].contact); SET_STR_VALUE(values+25, cell->legs[callee_leg].contact); CON_PS_REFERENCE(dialog_db_handle) = &my_ps_insert; if((dialog_dbf.insert(dialog_db_handle, insert_keys, values, DIALOG_TABLE_TOTAL_COL_NO)) !=0){ LM_ERR("could not add another dialog to db\n"); goto error; } /* dialog saved */ run_dlg_callbacks( DLGCB_SAVED, cell, 0, DLG_DIR_NONE, 0); cell->flags &= ~(DLG_FLAG_NEW|DLG_FLAG_CHANGED|DLG_FLAG_VP_CHANGED); } else if((cell->flags & DLG_FLAG_CHANGED) != 0) { /* save only dialog's state and timeout */ VAL_TYPE(values) = VAL_TYPE(values+1) = VAL_TYPE(values+12) = VAL_TYPE(values+13) = VAL_TYPE(values+16) = VAL_TYPE(values+17) = VAL_TYPE(values+18) = VAL_TYPE(values+21) =DB_INT; VAL_TYPE(values+14) = VAL_TYPE(values+15) = VAL_TYPE(values+19) = VAL_TYPE(values+20) = DB_STR; /* lock the entry */ entry = (d_table->entries)[cell->h_entry]; dlg_lock( d_table, &entry); SET_INT_VALUE(values, cell->h_entry); SET_INT_VALUE(values+1, cell->h_id); SET_INT_VALUE(values+12, cell->state); SET_INT_VALUE(values+13, (unsigned int)( (unsigned int)time(0) + cell->tl.timeout - get_ticks()) ); SET_STR_VALUE(values+14, cell->legs[DLG_CALLER_LEG].r_cseq); SET_STR_VALUE(values+15, cell->legs[callee_leg].r_cseq); SET_INT_VALUE(values+16,cell->legs[DLG_CALLER_LEG].last_gen_cseq); SET_INT_VALUE(values+17,cell->legs[callee_leg].last_gen_cseq); SET_INT_VALUE(values+18, cell->flags); set_final_update_cols(values+19, cell, 1); CON_PS_REFERENCE(dialog_db_handle) = &my_ps_update; if((dialog_dbf.update(dialog_db_handle, (insert_keys), 0, (values), (insert_keys+12), (values+12), 2, 10)) !=0){ LM_ERR("could not update database info\n"); goto error; } /* dialog saved */ run_dlg_callbacks( DLGCB_SAVED, cell, 0, DLG_DIR_NONE, 0); cell->flags &= ~(DLG_FLAG_CHANGED|DLG_FLAG_VP_CHANGED); } else if (cell->flags & DLG_FLAG_VP_CHANGED) { cell->flags |= DLG_FLAG_VP_CHANGED; VAL_TYPE(values) = VAL_TYPE(values+1) = VAL_TYPE(values+21) = DB_INT; VAL_TYPE(values+19) = VAL_TYPE(values+20) = DB_STR; /* lock the entry */ entry = (d_table->entries)[cell->h_entry]; dlg_lock( d_table, &entry); SET_INT_VALUE(values, cell->h_entry); SET_INT_VALUE(values+1, cell->h_id); set_final_update_cols(values+19, cell, 0); CON_PS_REFERENCE(dialog_db_handle) = &my_ps_update_vp; if((dialog_dbf.update(dialog_db_handle, (insert_keys), 0, (values), (insert_keys+19), (values+19), 2, 3)) !=0){ LM_ERR("could not update database info\n"); goto error; } run_dlg_callbacks( DLGCB_SAVED, cell, 0, DLG_DIR_NONE, 0); cell->flags &= ~DLG_FLAG_VP_CHANGED; } else { return 0; } dlg_unlock( d_table, &entry); return 0; error: dlg_unlock( d_table, &entry); return -1; }
dlg_t * build_dlg_t(struct dlg_cell * cell, int dst_leg, int src_leg) { dlg_t* td = NULL; str cseq; unsigned int loc_seq; td = (dlg_t*)pkg_malloc(sizeof(dlg_t)); if(!td){ LM_ERR("out of pkg memory\n"); return NULL; } memset(td, 0, sizeof(dlg_t)); if ((dst_leg == DLG_CALLER_LEG && (cell->flags & DLG_FLAG_PING_CALLER)) || (dst_leg == callee_idx(cell) && (cell->flags & DLG_FLAG_PING_CALLEE))) { dlg_lock_dlg(cell); if (cell->legs[dst_leg].last_gen_cseq == 0) { /* no OPTIONS pings for this dlg yet */ dlg_unlock_dlg(cell); goto before_strcseq; } else { /* OPTIONS pings sent, use new cseq */ td->loc_seq.value = ++(cell->legs[dst_leg].last_gen_cseq); td->loc_seq.is_set=1; dlg_unlock_dlg(cell); goto after_strcseq; } } before_strcseq: /*local sequence number*/ cseq = cell->legs[dst_leg].r_cseq; if( !cseq.s || !cseq.len || str2int(&cseq, &loc_seq) != 0){ LM_ERR("invalid cseq\n"); goto error; } /*we don not increase here the cseq as this will be done by TM*/ td->loc_seq.value = loc_seq; td->loc_seq.is_set = 1; after_strcseq: /*route set*/ if( cell->legs[dst_leg].route_set.s && cell->legs[dst_leg].route_set.len){ if( parse_rr_body(cell->legs[dst_leg].route_set.s, cell->legs[dst_leg].route_set.len, &td->route_set) !=0){ LM_ERR("failed to parse route set\n"); goto error; } } /*remote target--- Request URI*/ if (cell->legs[dst_leg].contact.s==0 || cell->legs[dst_leg].contact.len==0){ LM_ERR("no contact available\n"); goto error; } td->rem_target = cell->legs[dst_leg].contact; td->rem_uri = (dst_leg==DLG_CALLER_LEG)? *dlg_leg_from_uri(cell,dst_leg): *dlg_leg_to_uri(cell,dst_leg); td->loc_uri = (dst_leg==DLG_CALLER_LEG)? *dlg_leg_to_uri(cell,dst_leg): *dlg_leg_from_uri(cell,dst_leg); td->id.call_id = cell->callid; td->id.rem_tag = cell->legs[dst_leg].tag; td->id.loc_tag = cell->legs[src_leg].tag; td->state= DLG_CONFIRMED; td->send_sock = cell->legs[dst_leg].bind_addr; return td; error: free_tm_dlg(td); return NULL; }
static void dual_bye_event(struct dlg_cell* dlg, struct sip_msg *req, int extra_unref) { int event, old_state, new_state, unref, ret; struct dlg_cell *curr; event = DLG_EVENT_REQBYE; last_dst_leg = dlg->legs_no[DLG_LEG_200OK]; next_state_dlg(dlg, event, DLG_DIR_DOWNSTREAM, &old_state, &new_state, &unref, 0); unref += extra_unref; if(new_state == DLG_STATE_DELETED && old_state != DLG_STATE_DELETED){ LM_DBG("removing dialog with h_entry %u and h_id %u\n", dlg->h_entry, dlg->h_id); /*destroy linkers */ dlg_lock_dlg(dlg); destroy_linkers(dlg->profile_links, 0); dlg->profile_links = NULL; dlg_unlock_dlg(dlg); /* remove from timer */ ret = remove_dlg_timer(&dlg->tl); if (ret < 0) { LM_CRIT("unable to unlink the timer on dlg %p [%u:%u] " "with clid '%.*s' and tags '%.*s' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg_leg_print_info( dlg, DLG_CALLER_LEG, tag), dlg_leg_print_info( dlg, callee_idx(dlg), tag)); } else if (ret > 0) { LM_DBG("dlg already expired (not in timer list) %p [%u:%u] " "with clid '%.*s' and tags '%.*s' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg_leg_print_info( dlg, DLG_CALLER_LEG, tag), dlg_leg_print_info( dlg, callee_idx(dlg), tag)); } else { /* successfully removed from timer list */ unref++; } curr = current_dlg_pointer; current_dlg_pointer = dlg; /* dialog terminated (BYE) */ run_dlg_callbacks( DLGCB_TERMINATED, dlg, req, DLG_DIR_NONE, 0); current_dlg_pointer = curr; LM_DBG("first final reply\n"); /* derefering the dialog */ unref_dlg(dlg, unref); if_update_stat( dlg_enable_stats, active_dlgs, -1); } if(new_state == DLG_STATE_DELETED && old_state == DLG_STATE_DELETED ) { /* trash the dialog from DB and memory */ LM_DBG("second final reply\n"); /* delete the dialog from DB */ if (should_remove_dlg_db()) remove_dialog_from_db(dlg); /* force delete from mem */ unref_dlg(dlg, unref); } }
/** * replicates a confirmed dialog from another OpenSIPS instance * by reading the relevant information using the Binary Packet Interface */ int dlg_replicated_create(bin_packet_t *packet, struct dlg_cell *cell, str *ftag, str *ttag, int safe) { int h_entry; unsigned int dir, dst_leg; str callid = { NULL, 0 }, from_uri, to_uri, from_tag, to_tag; str cseq1, cseq2, contact1, contact2, rroute1, rroute2, mangled_fu, mangled_tu; str sock, vars, profiles; struct dlg_cell *dlg = NULL; struct socket_info *caller_sock, *callee_sock; struct dlg_entry *d_entry; LM_DBG("Received replicated dialog!\n"); if (!cell) { DLG_BIN_POP(str, packet, callid, malformed); DLG_BIN_POP(str, packet, from_tag, malformed); DLG_BIN_POP(str, packet, to_tag, malformed); DLG_BIN_POP(str, packet, from_uri, malformed); DLG_BIN_POP(str, packet, to_uri, malformed); dlg = get_dlg(&callid, &from_tag, &to_tag, &dir, &dst_leg); h_entry = dlg_hash(&callid); d_entry = &d_table->entries[h_entry]; if (safe) dlg_lock(d_table, d_entry); if (dlg) { LM_DBG("Dialog with ci '%.*s' is already created\n", callid.len, callid.s); unref_dlg_unsafe(dlg, 1, d_entry); /* unmark dlg as loaded from DB (otherwise it would have been * dropped later when syncing from cluster is done) */ dlg->flags &= ~DLG_FLAG_FROM_DB; dlg_unlock(d_table, d_entry); return 0; } dlg = build_new_dlg(&callid, &from_uri, &to_uri, &from_tag); if (!dlg) { LM_ERR("Failed to create replicated dialog!\n"); goto pre_linking_error; } } else { h_entry = dlg_hash(&cell->callid); d_entry = &d_table->entries[h_entry]; if (safe) dlg_lock(d_table, d_entry); from_tag = *ftag; to_tag = *ttag; dlg = cell; } if_update_stat(dlg_enable_stats, processed_dlgs, 1); DLG_BIN_POP(int, packet, dlg->h_id, pre_linking_error); DLG_BIN_POP(int, packet, dlg->start_ts, pre_linking_error); DLG_BIN_POP(int, packet, dlg->state, pre_linking_error); /* next_id follows the max value of all replicated ids */ if (d_table->entries[dlg->h_entry].next_id <= dlg->h_id) d_table->entries[dlg->h_entry].next_id = dlg->h_id + 1; DLG_BIN_POP(str, packet, sock, pre_linking_error); caller_sock = fetch_socket_info(&sock); DLG_BIN_POP(str, packet, sock, pre_linking_error); callee_sock = fetch_socket_info(&sock); if (!caller_sock || !callee_sock) { LM_ERR("Replicated dialog doesn't match any listening sockets\n"); goto pre_linking_error; } DLG_BIN_POP(str, packet, cseq1, pre_linking_error); DLG_BIN_POP(str, packet, cseq2, pre_linking_error); DLG_BIN_POP(str, packet, rroute1, pre_linking_error); DLG_BIN_POP(str, packet, rroute2, pre_linking_error); DLG_BIN_POP(str, packet, contact1, pre_linking_error); DLG_BIN_POP(str, packet, contact2, pre_linking_error); DLG_BIN_POP(str, packet, mangled_fu, pre_linking_error); DLG_BIN_POP(str, packet, mangled_tu, pre_linking_error); /* add the 2 legs */ /* TODO - sdp here */ if (dlg_update_leg_info(0, dlg, &from_tag, &rroute1, &contact1, &cseq1, caller_sock, 0, 0,0) != 0 || dlg_update_leg_info(1, dlg, &to_tag, &rroute2, &contact2, &cseq2, callee_sock, &mangled_fu, &mangled_tu,0) != 0) { LM_ERR("dlg_set_leg_info failed\n"); goto pre_linking_error; } dlg->legs_no[DLG_LEG_200OK] = DLG_FIRST_CALLEE_LEG; /* link the dialog into the hash */ if (!d_entry->first) d_entry->first = d_entry->last = dlg; else { d_entry->last->next = dlg; dlg->prev = d_entry->last; d_entry->last = dlg; } dlg->ref++; d_entry->cnt++; DLG_BIN_POP(str, packet, vars, pre_linking_error); DLG_BIN_POP(str, packet, profiles, pre_linking_error); DLG_BIN_POP(int, packet, dlg->user_flags, pre_linking_error); DLG_BIN_POP(int, packet, dlg->mod_flags, pre_linking_error); DLG_BIN_POP(int, packet, dlg->flags, pre_linking_error); /* also save the dialog into the DB on this instance */ dlg->flags |= DLG_FLAG_NEW; DLG_BIN_POP(int, packet, dlg->tl.timeout, pre_linking_error); DLG_BIN_POP(int, packet, dlg->legs[DLG_CALLER_LEG].last_gen_cseq, pre_linking_error); DLG_BIN_POP(int, packet, dlg->legs[callee_idx(dlg)].last_gen_cseq, pre_linking_error); if (dlg->tl.timeout <= (unsigned int) time(0)) dlg->tl.timeout = 0; else dlg->tl.timeout -= (unsigned int) time(0); /* restore the timer values */ if (insert_dlg_timer(&dlg->tl, (int) dlg->tl.timeout) != 0) { LM_CRIT("Unable to insert dlg %p [%u:%u] " "with clid '%.*s' and tags '%.*s' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg->legs[DLG_CALLER_LEG].tag.len, dlg->legs[DLG_CALLER_LEG].tag.s, dlg->legs[callee_idx(dlg)].tag.len, ZSW(dlg->legs[callee_idx(dlg)].tag.s)); goto error; } if (dlg->state == DLG_STATE_CONFIRMED_NA || dlg->state == DLG_STATE_CONFIRMED) active_dlgs_cnt++; /* reference the dialog as kept in the timer list */ ref_dlg_unsafe(dlg, 1); LM_DBG("Received initial timeout of %d for dialog %.*s, safe = %d\n", dlg->tl.timeout, callid.len, callid.s, safe); dlg->lifetime = 0; if (dlg->flags & DLG_FLAG_PING_CALLER || dlg->flags & DLG_FLAG_PING_CALLEE) { if (insert_ping_timer(dlg) != 0) LM_CRIT("Unable to insert dlg %p into ping timer\n",dlg); else { ref_dlg_unsafe(dlg, 1); } } if (dlg_db_mode == DB_MODE_DELAYED) { /* to be later removed by timer */ ref_dlg_unsafe(dlg, 1); } if (vars.s && vars.len != 0) read_dialog_vars(vars.s, vars.len, dlg); dlg_unlock(d_table, d_entry); if (profiles.s && profiles.len != 0) read_dialog_profiles(profiles.s, profiles.len, dlg, 0, 1); if_update_stat(dlg_enable_stats, active_dlgs, 1); run_load_callback_per_dlg(dlg); return 0; pre_linking_error: dlg_unlock(d_table, d_entry); if (dlg) destroy_dlg(dlg); return -1; error: dlg_unlock(d_table, d_entry); if (dlg) unref_dlg(dlg, 1); malformed: return -1; }
void rcv_cluster_event(enum clusterer_event ev, int node_id) { struct dlg_sharing_tag *tag; struct n_send_info *ni; int lock_old_flag; struct dlg_cell *dlg, *next_dlg; int i; int ret; int unref; if (ev == SYNC_REQ_RCV && receive_sync_request(node_id) < 0) LM_ERR("Failed to reply to sync request from node: %d\n", node_id); else if (ev == SYNC_DONE) { if (dlg_db_mode == DB_MODE_NONE) return; /* drop dialogs loaded from DB which have not been reconfirmed * through syncing or SIP(updates) */ for (i = 0; i < d_table->size; i++) { dlg_lock(d_table, &d_table->entries[i]); dlg = d_table->entries[i].first; while (dlg) { if (!(dlg->flags & DLG_FLAG_FROM_DB)) { dlg = dlg->next; continue; } LM_DBG("Drop DB loaded dialog ID=%lld\n", ((long long)dlg->h_entry << 32) | (dlg->h_id)); unref = 1; /* unref from hash */ dlg->state = DLG_STATE_DELETED; destroy_linkers(dlg->profile_links, 0); dlg->profile_links = NULL; /* remove from timer */ ret = remove_dlg_timer(&dlg->tl); if (ret < 0) { LM_ERR("unable to unlink the timer on dlg %p [%u:%u] " "with clid '%.*s' and tags '%.*s' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg_leg_print_info(dlg, DLG_CALLER_LEG, tag), dlg_leg_print_info(dlg, callee_idx(dlg), tag)); } else if (ret == 0) /* successfully removed from timer list */ unref++; if (dlg_db_mode != DB_MODE_SHUTDOWN) { dlg->flags &= ~DLG_FLAG_NEW; remove_dialog_from_db(dlg); dlg->flags |= DLG_FLAG_DB_DELETED; } if (dlg_db_mode == DB_MODE_DELAYED) unref++; next_dlg = dlg->next; unref_dlg_unsafe(dlg, unref, &d_table->entries[i]); dlg = next_dlg; } dlg_unlock(d_table, &d_table->entries[i]); } } else if (ev == CLUSTER_NODE_UP) { lock_start_sw_read(shtags_lock); for (tag = *shtags_list; tag; tag = tag->next) { if (!tag->send_active_msg) continue; /* send sharing tag active msg to nodes to which we didn't already */ for (ni = tag->active_msgs_sent; ni && ni->node_id != node_id; ni = ni->next) ; if (!ni) { if (send_shtag_active_info(&tag->name, node_id) < 0) { LM_ERR("Failed to send info about sharing tag\n"); continue; } ni = shm_malloc(sizeof *ni); if (!ni) { LM_ERR("No more shm memory!\n"); return; } ni->node_id = node_id; ni->next = tag->active_msgs_sent; lock_switch_write(shtags_lock, lock_old_flag); tag->active_msgs_sent = ni; lock_switch_read(shtags_lock, lock_old_flag); } } lock_stop_sw_read(shtags_lock); } }
/** * replicates a confirmed dialog from another OpenSIPS instance * by reading the relevant information using the Binary Packet Interface */ int dlg_replicated_create(struct dlg_cell *cell, str *ftag, str *ttag, int safe) { int next_id, h_entry; unsigned int dir, dst_leg; str callid, from_uri, to_uri, from_tag, to_tag; str cseq1,cseq2,contact1,contact2,rroute1,rroute2,mangled_fu,mangled_tu; str sock, vars, profiles; struct dlg_cell *dlg = NULL; struct socket_info *caller_sock, *callee_sock; struct dlg_entry *d_entry; if_update_stat(dlg_enable_stats, processed_dlgs, 1); LM_DBG("Received replicated dialog!\n"); if (!cell) { bin_pop_str(&callid); bin_pop_str(&from_tag); bin_pop_str(&to_tag); bin_pop_str(&from_uri); bin_pop_str(&to_uri); dlg = get_dlg(&callid, &from_tag, &to_tag, &dir, &dst_leg); h_entry = dlg_hash(&callid); d_entry = &d_table->entries[h_entry]; if (safe) dlg_lock(d_table, d_entry); if (dlg) { LM_DBG("Dialog with ci '%.*s' is already created\n", callid.len, callid.s); unref_dlg_unsafe(dlg, 1, d_entry); dlg_unlock(d_table, d_entry); return 0; } dlg = build_new_dlg(&callid, &from_uri, &to_uri, &from_tag); if (!dlg) { LM_ERR("Failed to create replicated dialog!\n"); goto pre_linking_error; } } else { h_entry = dlg_hash(&cell->callid); d_entry = &d_table->entries[h_entry]; if (safe) dlg_lock(d_table, d_entry); from_tag = *ftag; to_tag = *ttag; dlg = cell; } bin_pop_int(&dlg->h_id); bin_pop_int(&dlg->start_ts); bin_pop_int(&dlg->state); next_id = d_table->entries[dlg->h_entry].next_id; d_table->entries[dlg->h_entry].next_id = (next_id <= dlg->h_id) ? (dlg->h_id + 1) : next_id; if (bin_pop_str(&sock)) goto pre_linking_error; caller_sock = fetch_socket_info(&sock); if (bin_pop_str(&sock)) goto pre_linking_error; callee_sock = fetch_socket_info(&sock); if (!caller_sock || !callee_sock) { LM_ERR("Dialog in DB doesn't match any listening sockets\n"); goto pre_linking_error; } bin_pop_str(&cseq1); bin_pop_str(&cseq2); bin_pop_str(&rroute1); bin_pop_str(&rroute2); bin_pop_str(&contact1); bin_pop_str(&contact2); bin_pop_str(&mangled_fu); bin_pop_str(&mangled_tu); /* add the 2 legs */ if (dlg_add_leg_info(dlg, &from_tag, &rroute1, &contact1, &cseq1, caller_sock, 0, 0) != 0 || dlg_add_leg_info(dlg, &to_tag, &rroute2, &contact2, &cseq2, callee_sock, &mangled_fu, &mangled_tu) != 0) { LM_ERR("dlg_set_leg_info failed\n"); goto pre_linking_error; } dlg->legs_no[DLG_LEG_200OK] = DLG_FIRST_CALLEE_LEG; /* link the dialog into the hash */ dlg->h_id = d_entry->next_id++; if (!d_entry->first) d_entry->first = d_entry->last = dlg; else { d_entry->last->next = dlg; dlg->prev = d_entry->last; d_entry->last = dlg; } dlg->ref++; d_entry->cnt++; bin_pop_str(&vars); bin_pop_str(&profiles); bin_pop_int(&dlg->user_flags); bin_pop_int(&dlg->flags); bin_pop_int((void *)&dlg->tl.timeout); bin_pop_int(&dlg->legs[DLG_CALLER_LEG].last_gen_cseq); bin_pop_int(&dlg->legs[callee_idx(dlg)].last_gen_cseq); if (dlg->tl.timeout <= (unsigned int)time(0)) dlg->tl.timeout = 0; else dlg->tl.timeout -= (unsigned int)time(0); /* restore the timer values */ if (insert_dlg_timer(&dlg->tl, (int)dlg->tl.timeout) != 0) { LM_CRIT("Unable to insert dlg %p [%u:%u] " "with clid '%.*s' and tags '%.*s' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg->legs[DLG_CALLER_LEG].tag.len, dlg->legs[DLG_CALLER_LEG].tag.s, dlg->legs[callee_idx(dlg)].tag.len, ZSW(dlg->legs[callee_idx(dlg)].tag.s)); goto error; } if (dlg->state == DLG_STATE_CONFIRMED_NA || dlg->state == DLG_STATE_CONFIRMED) active_dlgs_cnt++; /* reference the dialog as kept in the timer list */ ref_dlg_unsafe(dlg, 1); LM_DBG("Received initial timeout of %d for dialog %.*s, safe = %d\n",dlg->tl.timeout,callid.len,callid.s,safe); dlg->lifetime = 0; /* Do not replicate the pinging - we might terminate dialogs badly when running as backup if (dlg->flags & DLG_FLAG_PING_CALLER || dlg->flags & DLG_FLAG_PING_CALLEE) { if (insert_ping_timer(dlg) != 0) LM_CRIT("Unable to insert dlg %p into ping timer\n",dlg); else { ref_dlg_unsafe(dlg, 1); } } */ if (dlg_db_mode == DB_MODE_DELAYED) { /* to be later removed by timer */ ref_dlg_unsafe(dlg, 1); } if (vars.s && vars.len != 0) read_dialog_vars(vars.s, vars.len, dlg); dlg_unlock(d_table, d_entry); if (profiles.s && profiles.len != 0) read_dialog_profiles(profiles.s, profiles.len, dlg, 0, 1); if_update_stat(dlg_enable_stats, active_dlgs, 1); run_load_callback_per_dlg(dlg); return 0; pre_linking_error: dlg_unlock(d_table, d_entry); if (dlg) destroy_dlg(dlg); return -1; error: dlg_unlock(d_table, d_entry); if (dlg) unref_dlg(dlg, 1); return -1; }
/** * replicates the remote deletion of a dialog locally * by reading the relevant information using the Binary Packet Interface */ int dlg_replicated_delete(bin_packet_t *packet) { str call_id, from_tag, to_tag; unsigned int dir, dst_leg; struct dlg_cell *dlg; int old_state, new_state, unref, ret; DLG_BIN_POP(str, packet, call_id, malformed); DLG_BIN_POP(str, packet, from_tag, malformed); DLG_BIN_POP(str, packet, to_tag, malformed); LM_DBG("Deleting dialog with callid: %.*s\n", call_id.len, call_id.s); dlg = get_dlg(&call_id, &from_tag, &to_tag, &dir, &dst_leg); if (!dlg) { /* may be already deleted due to timeout */ LM_DBG("dialog not found (callid: |%.*s| ftag: |%.*s|\n", call_id.len, call_id.s, from_tag.len, from_tag.s); return 0; } dlg_lock_dlg(dlg); destroy_linkers(dlg->profile_links, 1); dlg->profile_links = NULL; dlg_unlock_dlg(dlg); /* simulate BYE received from caller */ next_state_dlg(dlg, DLG_EVENT_REQBYE, DLG_DIR_DOWNSTREAM, &old_state, &new_state, &unref, dlg->legs_no[DLG_LEG_200OK], 0); if (old_state == new_state) { LM_ERR("duplicate dialog delete request (callid: |%.*s|" "ftag: |%.*s|\n", call_id.len, call_id.s, from_tag.len, from_tag.s); return -1; } ret = remove_dlg_timer(&dlg->tl); if (ret < 0) { LM_CRIT("unable to unlink the timer on dlg %p [%u:%u] " "with clid '%.*s' and tags '%.*s' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg->legs[DLG_CALLER_LEG].tag.len, dlg->legs[DLG_CALLER_LEG].tag.s, dlg->legs[callee_idx(dlg)].tag.len, ZSW(dlg->legs[callee_idx(dlg)].tag.s)); } else if (ret > 0) { LM_DBG("dlg expired (not in timer list) on dlg %p [%u:%u] " "with clid '%.*s' and tags '%.*s' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg->legs[DLG_CALLER_LEG].tag.len, dlg->legs[DLG_CALLER_LEG].tag.s, dlg->legs[callee_idx(dlg)].tag.len, ZSW(dlg->legs[callee_idx(dlg)].tag.s)); } else { /* dialog successfully removed from timer -> unref */ unref++; } unref_dlg(dlg, 1 + unref); if_update_stat(dlg_enable_stats, active_dlgs, -1); return 0; malformed: return -1; }