Index: channels/chan_sip.c =================================================================== --- channels/chan_sip.c (revision 177657) +++ channels/chan_sip.c (working copy) @@ -1061,6 +1061,7 @@ int callevents; /*!< Whether we send manager events or not */ int regextenonqualify; /*!< Whether to add/remove regexten when qualifying peers */ int matchexterniplocally; /*!< Match externip/externhost setting against localnet setting */ + int allow_forking; /*!< Whether to allow forking or not */ int notifyringing; /*!< Send notifications on ringing */ int notifyhold; /*!< Send notifications on hold */ enum notifycid_setting notifycid; /*!< Send CID with ringing notifications */ @@ -1714,6 +1715,7 @@ int hangupcause; /*!< Storage of hangupcause copied from our owner before we disconnect from the AST channel (only used at hangup) */ struct sip_subscription_mwi *mwi; /*!< If this is a subscription MWI dialog, to which subscription */ + struct sip_peer *peer; /*!< associated peer */ }; @@ -1839,6 +1841,7 @@ AST_STRING_FIELD(mohsuggest); /*!< Music on Hold class */ AST_STRING_FIELD(parkinglot); /*!< Parkinglot */ AST_STRING_FIELD(useragent); /*!< User agent in SIP request (saved from registration) */ + AST_STRING_FIELD(callid); /*!< callid, used to identify registration refreshes */ ); struct sip_socket socket; /*!< Socket used for this peer */ unsigned int transports:3; /*!< Transports (enum sip_transport) that are acceptable for this peer */ @@ -1895,7 +1898,7 @@ int timer_t1; /*!< The maximum T1 value for the peer */ int timer_b; /*!< The maximum timer B (transaction timeouts) */ int deprecated_username; /*!< If it's a realtime peer, are they using the deprecated "username" instead of "defaultuser" */ - + AST_LIST_ENTRY(sip_peer) list; /* multiple registration entries for same user*/ /*XXX Seems like we suddenly have two flags with the same content. Why? To be continued... */ enum sip_peer_type type; /*!< Distinguish between "user" and "peer" types. This is used solely for CLI and manager commands */ char onlymatchonip; /*!< Only match on IP for incoming calls (old type=peer) */ @@ -2331,7 +2334,8 @@ static void ast_quiet_chan(struct ast_channel *chan); static int attempt_transfer(struct sip_dual *transferer, struct sip_dual *target); static int do_magic_pickup(struct ast_channel *channel, const char *extension, const char *context); - +/* add peer info to db */ +static int sip_db_add(struct sip_peer *peer, int expire, char* data, int nodepos); /*! * \brief generic function for determining if a correct transport is being * used to contact a peer @@ -2437,18 +2441,18 @@ static void sip_dump_history(struct sip_pvt *dialog); /*--- Device object handling */ +static struct sip_peer *sip_allocpeer(const char *name); static struct sip_peer *temp_peer(const char *name); static struct sip_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int realtime, int ispeer); static int update_call_counter(struct sip_pvt *fup, int event); static void sip_destroy_peer(struct sip_peer *peer); static void sip_destroy_peer_fn(void *peer); static void set_peer_defaults(struct sip_peer *peer); -static struct sip_peer *temp_peer(const char *name); static void register_peer_exten(struct sip_peer *peer, int onoff); static struct sip_peer *find_peer(const char *peer, struct sockaddr_in *sin, int realtime, int forcenamematch, int devstate_only); static int sip_poke_peer_s(const void *data); static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, struct sip_peer *p, struct sip_request *req); -static void reg_source_db(struct sip_peer *peer); +static void reg_source_db(struct sip_peer *peer, int flag); static void destroy_association(struct sip_peer *peer); static void set_insecure_flags(struct ast_flags *flags, const char *value, int lineno); static int handle_common_options(struct ast_flags *flags, struct ast_flags *mask, struct ast_variable *v); @@ -4484,6 +4488,7 @@ if (ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS) && !devstate_only) { /* Cache peer */ + struct sip_peer *tmppeer = peer; ast_copy_flags(&peer->flags[1], &global_flags[1], SIP_PAGE2_RTAUTOCLEAR|SIP_PAGE2_RTCACHEFRIENDS); if (ast_test_flag(&global_flags[1], SIP_PAGE2_RTAUTOCLEAR)) { AST_SCHED_REPLACE_UNREF(peer->expire, sched, sip_cfg.rtautoclear * 1000, expire_register, peer, @@ -4491,10 +4496,13 @@ unref_peer(peer, "remove registration ref"), ref_peer(peer, "add registration ref")); } - ao2_t_link(peers, peer, "link peer into peers table"); - if (peer->addr.sin_addr.s_addr) { - ao2_t_link(peers_by_ip, peer, "link peer into peers_by_ip table"); - } + while(tmppeer) { + ao2_t_link(peers, tmppeer, "link peer into peers table"); + if (tmppeer->addr.sin_addr.s_addr) { + ao2_t_link(peers_by_ip, tmppeer, "link peer into peers_by_ip table"); + } + tmppeer = tmppeer->list.next; + } } peer->is_realtime = 1; if (peerlist) @@ -4837,8 +4845,10 @@ dialog->sa.sin_family = AF_INET; dialog->timer_t1 = global_t1; /* Default SIP retransmission timer T1 (RFC 3261) */ dialog->timer_b = global_timer_b; /* Default SIP transaction timer B (RFC 3261) */ - peer = find_peer(peername, NULL, TRUE, FINDALLDEVICES, FALSE); + /*peer = find_peer(peername, NULL, TRUE, FINDALLDEVICES, FALSE); */ + peer = dialog->peer; + if (peer) { int res; if (newdialog) @@ -4849,7 +4859,7 @@ dialog->sa.sin_port = dialog->recv.sin_port = htons(portno); } } - unref_peer(peer, "create_addr: unref peer from find_peer hashtab lookup"); +// unref_peer(peer, "create_addr: unref peer from find_peer hashtab lookup"); return res; } @@ -4945,89 +4955,101 @@ struct varshead *headp; struct ast_var_t *current; const char *referer = NULL; /* SIP referrer */ + struct sip_pvt *tmpp = NULL; /*temp var*/ if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) { ast_log(LOG_WARNING, "sip_call called on %s, neither down nor reserved\n", ast->name); return -1; } - /* Check whether there is vxml_url, distinctive ring variables */ - headp=&ast->varshead; - AST_LIST_TRAVERSE(headp, current, entries) { - /* Check whether there is a VXML_URL variable */ - if (!p->options->vxml_url && !strcasecmp(ast_var_name(current), "VXML_URL")) { - p->options->vxml_url = ast_var_value(current); - } else if (!p->options->uri_options && !strcasecmp(ast_var_name(current), "SIP_URI_OPTIONS")) { - p->options->uri_options = ast_var_value(current); - } else if (!p->options->addsipheaders && !strncasecmp(ast_var_name(current), "SIPADDHEADER", strlen("SIPADDHEADER"))) { - /* Check whether there is a variable with a name starting with SIPADDHEADER */ - p->options->addsipheaders = 1; - } else if (!strcasecmp(ast_var_name(current), "SIPFROMDOMAIN")) { - ast_string_field_set(p, fromdomain, ast_var_value(current)); - } else if (!strcasecmp(ast_var_name(current), "SIPTRANSFER")) { - /* This is a transfered call */ - p->options->transfer = 1; - } else if (!strcasecmp(ast_var_name(current), "SIPTRANSFER_REFERER")) { - /* This is the referrer */ - referer = ast_var_value(current); - } else if (!strcasecmp(ast_var_name(current), "SIPTRANSFER_REPLACES")) { - /* We're replacing a call. */ - p->options->replaces = ast_var_value(current); - } else if (!strcasecmp(ast_var_name(current), "T38CALL")) { - p->t38.state = T38_LOCAL_DIRECT; - ast_debug(1, "T38State change to %d on channel %s\n", p->t38.state, ast->name); - } - } + /* If there is a list of sip_pvt, loop thru all of them, we will have to fork*/ + tmpp = p; + if(!p) { + ast_log(LOG_WARNING, "sip_pvt is NULL\n"); + return -1; + } - res = 0; - ast_set_flag(&p->flags[0], SIP_OUTGOING); + do { + /* Check whether there is vxml_url, distinctive ring variables */ + headp=&ast->varshead; + AST_LIST_TRAVERSE(headp, current, entries) { + /* Check whether there is a VXML_URL variable */ + if (!p->options->vxml_url && !strcasecmp(ast_var_name(current), "VXML_URL")) { + p->options->vxml_url = ast_var_value(current); + } else if (!p->options->uri_options && !strcasecmp(ast_var_name(current), "SIP_URI_OPTIONS")) { + p->options->uri_options = ast_var_value(current); + } else if (!p->options->addsipheaders && !strncasecmp(ast_var_name(current), "SIPADDHEADER", strlen("SIPADDHEADER"))) { + /* Check whether there is a variable with a name starting with SIPADDHEADER */ + p->options->addsipheaders = 1; + } else if (!strcasecmp(ast_var_name(current), "SIPFROMDOMAIN")) { + ast_string_field_set(p, fromdomain, ast_var_value(current)); + } else if (!strcasecmp(ast_var_name(current), "SIPTRANSFER")) { + /* This is a transfered call */ + p->options->transfer = 1; + } else if (!strcasecmp(ast_var_name(current), "SIPTRANSFER_REFERER")) { + /* This is the referrer */ + referer = ast_var_value(current); + } else if (!strcasecmp(ast_var_name(current), "SIPTRANSFER_REPLACES")) { + /* We're replacing a call. */ + p->options->replaces = ast_var_value(current); + } else if (!strcasecmp(ast_var_name(current), "T38CALL")) { + p->t38.state = T38_LOCAL_DIRECT; + ast_debug(1, "T38State change to %d on channel %s\n", p->t38.state, ast->name); + } + } - if (p->options->transfer) { - char buf[SIPBUFSIZE/2]; + res = 0; + ast_set_flag(&p->flags[0], SIP_OUTGOING); - if (referer) { - if (sipdebug) - ast_debug(3, "Call for %s transfered by %s\n", p->username, referer); - snprintf(buf, sizeof(buf)-1, "-> %s (via %s)", p->cid_name, referer); - } else - snprintf(buf, sizeof(buf)-1, "-> %s", p->cid_name); - ast_string_field_set(p, cid_name, buf); - } - ast_debug(1, "Outgoing Call for %s\n", p->username); + if (p->options->transfer) { + char buf[SIPBUFSIZE/2]; - res = update_call_counter(p, INC_CALL_RINGING); + if (referer) { + if (sipdebug) + ast_debug(3, "Call for %s transfered by %s\n", p->username, referer); + snprintf(buf, sizeof(buf)-1, "-> %s (via %s)", p->cid_name, referer); + } else + snprintf(buf, sizeof(buf)-1, "-> %s", p->cid_name); + ast_string_field_set(p, cid_name, buf); + } + ast_debug(1, "Outgoing Call for %s\n", p->username); - if (res == -1) { - return res; - } else { - ast->hangupcause = AST_CAUSE_USER_BUSY; - } + res = update_call_counter(p, INC_CALL_RINGING); - p->callingpres = ast->cid.cid_pres; - p->jointcapability = ast_translate_available_formats(p->capability, p->prefcodec); - p->jointnoncodeccapability = p->noncodeccapability; + if (res == -1) { + return res; + } else { + ast->hangupcause = AST_CAUSE_USER_BUSY; + } - /* If there are no audio formats left to offer, punt */ - if (!(p->jointcapability & AST_FORMAT_AUDIO_MASK)) { - ast_log(LOG_WARNING, "No audio format found to offer. Cancelling call to %s\n", p->username); - res = -1; - } else { - int xmitres; + p->callingpres = ast->cid.cid_pres; + p->jointcapability = ast_translate_available_formats(p->capability, p->prefcodec); + p->jointnoncodeccapability = p->noncodeccapability; - p->t38.jointcapability = p->t38.capability; - ast_debug(2, "Our T38 capability (%d), joint T38 capability (%d)\n", p->t38.capability, p->t38.jointcapability); + /* If there are no audio formats left to offer, punt */ + if (!(p->jointcapability & AST_FORMAT_AUDIO_MASK)) { + ast_log(LOG_WARNING, "No audio format found to offer. Cancelling call to %s\n", p->username); + res = -1; + } else { + int xmitres; - xmitres = transmit_invite(p, SIP_INVITE, 1, 2); - if (xmitres == XMIT_ERROR) - return -1; - p->invitestate = INV_CALLING; + p->t38.jointcapability = p->t38.capability; + ast_debug(2, "Our T38 capability (%d), joint T38 capability (%d)\n", p->t38.capability, p->t38.jointcapability); + + xmitres = transmit_invite(p, SIP_INVITE, 1, 2); + if (xmitres == XMIT_ERROR) + return -1; + p->invitestate = INV_CALLING; - /* Initialize auto-congest time */ - AST_SCHED_REPLACE_UNREF(p->initid, sched, p->timer_b, auto_congest, p, - dialog_unref(_data, "dialog ptr dec when SCHED_REPLACE del op succeeded"), - dialog_unref(p, "dialog ptr dec when SCHED_REPLACE add failed"), - dialog_ref(p, "dialog ptr inc when SCHED_REPLACE add succeeded") ); - } + /* Initialize auto-congest time */ + AST_SCHED_REPLACE_UNREF(p->initid, sched, p->timer_b, auto_congest, p, + dialog_unref(_data, "dialog ptr dec when SCHED_REPLACE del op succeeded"), + dialog_unref(p, "dialog ptr dec when SCHED_REPLACE add failed"), + dialog_ref(p, "dialog ptr inc when SCHED_REPLACE add succeeded") ); + } + tmpp = tmpp->next; + p = tmpp; + } while (tmpp); return res; } @@ -5087,12 +5109,15 @@ if (p->owner) { if (lockowner) ast_channel_lock(p->owner); - if (option_debug) - ast_log(LOG_DEBUG, "Detaching from %s\n", p->owner->name); - p->owner->tech_pvt = NULL; - /* Make sure that the channel knows its backend is going away */ - p->owner->_softhangup |= AST_SOFTHANGUP_DEV; - if (lockowner) + if(p->owner->no_of_pvt_channels < 1) + { + if (option_debug) + ast_log(LOG_DEBUG, "Detaching from %s\n", p->owner->name); + p->owner->tech_pvt = NULL; + /* Make sure that the channel knows its backend is going away */ + p->owner->_softhangup |= AST_SOFTHANGUP_DEV; + } + if (lockowner) ast_channel_unlock(p->owner); /* Give the channel a chance to react before deallocation */ usleep(1); @@ -5529,188 +5554,202 @@ int needcancel = FALSE; int needdestroy = 0; struct ast_channel *oldowner = ast; + int hangup = 0; - if (!p) { - ast_debug(1, "Asked to hangup channel that was not connected\n"); - return 0; - } - if (ast_test_flag(ast, AST_FLAG_ANSWERED_ELSEWHERE) || ast->hangupcause == AST_CAUSE_ANSWERED_ELSEWHERE) { - ast_debug(1, "This call was answered elsewhere"); - if (ast->hangupcause == AST_CAUSE_ANSWERED_ELSEWHERE) { - ast_debug(1, "####### It's the cause code, buddy. The cause code!!!\n"); - } - append_history(p, "Cancel", "Call answered elsewhere"); - p->answered_elsewhere = TRUE; - } + /* if there are multiple calls in progress, due to forking, loop thru all and disconnect */ + do { + if (!p) { + ast_debug(1, "Asked to hangup channel that was not connected\n"); + return 0; + } + + if (p->owner) { + /* decrement no of pvt channels under owner channel */ + p->owner->no_of_pvt_channels--; + if(p->owner->no_of_pvt_channels < 1) + hangup = 1; /* if this is the last sip_pvt under owner, you can finally hangup */ + } + + if (ast_test_flag(ast, AST_FLAG_ANSWERED_ELSEWHERE) || ast->hangupcause == AST_CAUSE_ANSWERED_ELSEWHERE) { + ast_debug(1, "This call was answered elsewhere"); + if (ast->hangupcause == AST_CAUSE_ANSWERED_ELSEWHERE) { + ast_debug(1, "####### It's the cause code, buddy. The cause code!!!\n"); + } + append_history(p, "Cancel", "Call answered elsewhere"); + p->answered_elsewhere = TRUE; + } - /* Store hangupcause locally in PVT so we still have it before disconnect */ - if (p->owner) - p->hangupcause = p->owner->hangupcause; + /* Store hangupcause locally in PVT so we still have it before disconnect */ + if (p->owner) + p->hangupcause = p->owner->hangupcause; - if (ast_test_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER)) { - if (ast_test_flag(&p->flags[0], SIP_INC_COUNT) || ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD)) { - if (sipdebug) - ast_debug(1, "update_call_counter(%s) - decrement call limit counter on hangup\n", p->username); - update_call_counter(p, DEC_CALL_LIMIT); - } - ast_debug(4, "SIP Transfer: Not hanging up right now... Rescheduling hangup for %s.\n", p->callid); - if (p->autokillid > -1 && sip_cancel_destroy(p)) - ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n"); - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - ast_clear_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER); /* Really hang up next time */ - p->needdestroy = 0; - p->owner->tech_pvt = dialog_unref(p->owner->tech_pvt, "unref p->owner->tech_pvt"); - sip_pvt_lock(p); - p->owner = NULL; /* Owner will be gone after we return, so take it away */ - sip_pvt_unlock(p); - return 0; - } + if (ast_test_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER)) { + if (ast_test_flag(&p->flags[0], SIP_INC_COUNT) || ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD)) { + if (sipdebug) + ast_debug(1, "update_call_counter(%s) - decrement call limit counter on hangup\n", p->username); + update_call_counter(p, DEC_CALL_LIMIT); + } + ast_debug(4, "SIP Transfer: Not hanging up right now... Rescheduling hangup for %s.\n", p->callid); + if (p->autokillid > -1 && sip_cancel_destroy(p)) + ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n"); + sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); + ast_clear_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER); /* Really hang up next time */ + p->needdestroy = 0; + p->owner->tech_pvt = dialog_unref(p->owner->tech_pvt, "unref p->owner->tech_pvt"); + sip_pvt_lock(p); + p->owner = NULL; /* Owner will be gone after we return, so take it away */ + sip_pvt_unlock(p); + return 0; + } - if (ast_test_flag(ast, AST_FLAG_ZOMBIE)) { - if (p->refer) - ast_debug(1, "SIP Transfer: Hanging up Zombie channel %s after transfer ... Call-ID: %s\n", ast->name, p->callid); - else - ast_debug(1, "Hanging up zombie call. Be scared.\n"); - } else - ast_debug(1, "Hangup call %s, SIP callid %s\n", ast->name, p->callid); + if (ast_test_flag(ast, AST_FLAG_ZOMBIE)) { + if (p->refer) + ast_debug(1, "SIP Transfer: Hanging up Zombie channel %s after transfer ... Call-ID: %s\n", ast->name, p->callid); + else + ast_debug(1, "Hanging up zombie call. Be scared.\n"); + } else + ast_debug(1, "Hangup call %s, SIP callid %s\n", ast->name, p->callid); - sip_pvt_lock(p); - if (ast_test_flag(&p->flags[0], SIP_INC_COUNT) || ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD)) { - if (sipdebug) - ast_debug(1, "update_call_counter(%s) - decrement call limit counter on hangup\n", p->username); - update_call_counter(p, DEC_CALL_LIMIT); - } + sip_pvt_lock(p); + if (ast_test_flag(&p->flags[0], SIP_INC_COUNT) || ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD)) { + if (sipdebug) + ast_debug(1, "update_call_counter(%s) - decrement call limit counter on hangup\n", p->username); + update_call_counter(p, DEC_CALL_LIMIT); + } - /* Determine how to disconnect */ - if (p->owner != ast) { - ast_log(LOG_WARNING, "Huh? We aren't the owner? Can't hangup call.\n"); - sip_pvt_unlock(p); - return 0; - } - /* If the call is not UP, we need to send CANCEL instead of BYE */ - /* In case of re-invites, the call might be UP even though we have an incomplete invite transaction */ - if (p->invitestate < INV_COMPLETED && p->owner->_state != AST_STATE_UP) { - needcancel = TRUE; - ast_debug(4, "Hanging up channel in state %s (not UP)\n", ast_state2str(ast->_state)); - } + /* Determine how to disconnect */ + if (p->owner != ast) { + ast_log(LOG_WARNING, "Huh? We aren't the owner? Can't hangup call.\n"); + sip_pvt_unlock(p); + return 0; + } + /* If the call is not UP, we need to send CANCEL instead of BYE */ + /* In case of re-invites, the call might be UP even though we have an incomplete invite transaction */ + if (p->invitestate < INV_COMPLETED && p->owner->_state != AST_STATE_UP) { + needcancel = TRUE; + ast_debug(4, "Hanging up channel in state %s (not UP)\n", ast_state2str(ast->_state)); + } - stop_media_flows(p); /* Immediately stop RTP, VRTP and UDPTL as applicable */ + stop_media_flows(p); /* Immediately stop RTP, VRTP and UDPTL as applicable */ - append_history(p, needcancel ? "Cancel" : "Hangup", "Cause %s", p->owner ? ast_cause2str(p->hangupcause) : "Unknown"); + append_history(p, needcancel ? "Cancel" : "Hangup", "Cause %s", p->owner ? ast_cause2str(p->hangupcause) : "Unknown"); - /* Disconnect */ - if (p->vad) - ast_dsp_free(p->vad); + /* Disconnect */ + if (p->vad) + ast_dsp_free(p->vad); - p->owner = NULL; - ast->tech_pvt = dialog_unref(ast->tech_pvt, "unref ast->tech_pvt"); + p->owner = NULL; + ast->tech_pvt = dialog_unref(ast->tech_pvt, "unref ast->tech_pvt"); - ast_module_unref(ast_module_info->self); - /* Do not destroy this pvt until we have timeout or - get an answer to the BYE or INVITE/CANCEL - If we get no answer during retransmit period, drop the call anyway. - (Sorry, mother-in-law, you can't deny a hangup by sending - 603 declined to BYE...) - */ - if (p->alreadygone) - needdestroy = 1; /* Set destroy flag at end of this function */ - else if (p->invitestate != INV_CALLING) - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); + ast_module_unref(ast_module_info->self); + /* Do not destroy this pvt until we have timeout or + get an answer to the BYE or INVITE/CANCEL + If we get no answer during retransmit period, drop the call anyway. + (Sorry, mother-in-law, you can't deny a hangup by sending + 603 declined to BYE...) + */ + if (p->alreadygone) + needdestroy = 1; /* Set destroy flag at end of this function */ + else if (p->invitestate != INV_CALLING) + sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - /* Start the process if it's not already started */ - if (!p->alreadygone && p->initreq.data && !ast_strlen_zero(p->initreq.data->str)) { - if (needcancel) { /* Outgoing call, not up */ - if (ast_test_flag(&p->flags[0], SIP_OUTGOING)) { - /* stop retransmitting an INVITE that has not received a response */ - __sip_pretend_ack(p); + /* Start the process if it's not already started */ + if (!p->alreadygone && p->initreq.data && !ast_strlen_zero(p->initreq.data->str)) { + if (needcancel) { /* Outgoing call, not up */ + if (ast_test_flag(&p->flags[0], SIP_OUTGOING)) { + /* stop retransmitting an INVITE that has not received a response */ + __sip_pretend_ack(p); - /* if we can't send right now, mark it pending */ - if (p->invitestate == INV_CALLING) { - /* We can't send anything in CALLING state */ - ast_set_flag(&p->flags[0], SIP_PENDINGBYE); - /* Do we need a timer here if we don't hear from them at all? */ - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - append_history(p, "DELAY", "Not sending cancel, waiting for timeout"); - } else { - p->invitestate = INV_CANCELLED; - /* Send a new request: CANCEL */ - transmit_request(p, SIP_CANCEL, p->lastinvite, XMIT_RELIABLE, FALSE); - /* Actually don't destroy us yet, wait for the 487 on our original - INVITE, but do set an autodestruct just in case we never get it. */ - needdestroy = 0; - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - } - if ( p->initid != -1 ) { - /* channel still up - reverse dec of inUse counter - only if the channel is not auto-congested */ - update_call_counter(p, INC_CALL_LIMIT); - } - } else { /* Incoming call, not up */ - const char *res; - if (p->hangupcause && (res = hangup_cause2sip(p->hangupcause))) - transmit_response_reliable(p, res, &p->initreq); - else - transmit_response_reliable(p, "603 Declined", &p->initreq); - p->invitestate = INV_TERMINATED; - } - } else { /* Call is in UP state, send BYE */ - if (p->stimer->st_active == TRUE) { - stop_session_timer(p); - } + /* if we can't send right now, mark it pending */ + if (p->invitestate == INV_CALLING) { + /* We can't send anything in CALLING state */ + ast_set_flag(&p->flags[0], SIP_PENDINGBYE); + /* Do we need a timer here if we don't hear from them at all? */ + sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); + append_history(p, "DELAY", "Not sending cancel, waiting for timeout"); + } else { + p->invitestate = INV_CANCELLED; + /* Send a new request: CANCEL */ + transmit_request(p, SIP_CANCEL, p->lastinvite, XMIT_RELIABLE, FALSE); + /* Actually don't destroy us yet, wait for the 487 on our original + INVITE, but do set an autodestruct just in case we never get it. */ + needdestroy = 0; + sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); + } + if ( p->initid != -1 ) { + /* channel still up - reverse dec of inUse counter + only if the channel is not auto-congested */ + update_call_counter(p, INC_CALL_LIMIT); + } + } else { /* Incoming call, not up */ + const char *res; + if (p->hangupcause && (res = hangup_cause2sip(p->hangupcause))) + transmit_response_reliable(p, res, &p->initreq); + else + transmit_response_reliable(p, "603 Declined", &p->initreq); + p->invitestate = INV_TERMINATED; + } + } else { /* Call is in UP state, send BYE */ + if (p->stimer->st_active == TRUE) { + stop_session_timer(p); + } - if (!p->pendinginvite) { - struct ast_channel *bridge = ast_bridged_channel(oldowner); - char *audioqos = ""; - char *videoqos = ""; - char *textqos = ""; + if (!p->pendinginvite) { + struct ast_channel *bridge = ast_bridged_channel(oldowner); + char *audioqos = ""; + char *videoqos = ""; + char *textqos = ""; - if (p->rtp) - ast_rtp_set_vars(oldowner, p->rtp); + if (p->rtp) + ast_rtp_set_vars(oldowner, p->rtp); - if (bridge) { - struct sip_pvt *q = bridge->tech_pvt; + if (bridge) { + struct sip_pvt *q = bridge->tech_pvt; - if (IS_SIP_TECH(bridge->tech) && q) - ast_rtp_set_vars(bridge, q->rtp); - } + if (IS_SIP_TECH(bridge->tech) && q) + ast_rtp_set_vars(bridge, q->rtp); + } - if (p->vrtp) - videoqos = ast_rtp_get_quality(p->vrtp, NULL, RTPQOS_SUMMARY); - if (p->trtp) - textqos = ast_rtp_get_quality(p->trtp, NULL, RTPQOS_SUMMARY); - /* Send a hangup */ - transmit_request_with_auth(p, SIP_BYE, 0, XMIT_RELIABLE, 1); + if (p->vrtp) + videoqos = ast_rtp_get_quality(p->vrtp, NULL, RTPQOS_SUMMARY); + if (p->trtp) + textqos = ast_rtp_get_quality(p->trtp, NULL, RTPQOS_SUMMARY); + /* Send a hangup */ + transmit_request_with_auth(p, SIP_BYE, 0, XMIT_RELIABLE, 1); - /* Get RTCP quality before end of call */ - if (p->do_history) { - if (p->rtp) - append_history(p, "RTCPaudio", "Quality:%s", audioqos); - if (p->vrtp) - append_history(p, "RTCPvideo", "Quality:%s", videoqos); - if (p->trtp) - append_history(p, "RTCPtext", "Quality:%s", textqos); - } - if (p->rtp && oldowner) - pbx_builtin_setvar_helper(oldowner, "RTPAUDIOQOS", audioqos); - if (p->vrtp && oldowner) - pbx_builtin_setvar_helper(oldowner, "RTPVIDEOQOS", videoqos); - if (p->trtp && oldowner) - pbx_builtin_setvar_helper(oldowner, "RTPTEXTQOS", textqos); - } else { - /* Note we will need a BYE when this all settles out - but we can't send one while we have "INVITE" outstanding. */ - ast_set_flag(&p->flags[0], SIP_PENDINGBYE); - ast_clear_flag(&p->flags[0], SIP_NEEDREINVITE); - AST_SCHED_DEL_UNREF(sched, p->waitid, dialog_unref(p, "when you delete the waitid sched, you should dec the refcount for the stored dialog ptr")); - if (sip_cancel_destroy(p)) - ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n"); - } - } - } - if (needdestroy) { - pvt_set_needdestroy(p, "hangup"); - } - sip_pvt_unlock(p); + /* Get RTCP quality before end of call */ + if (p->do_history) { + if (p->rtp) + append_history(p, "RTCPaudio", "Quality:%s", audioqos); + if (p->vrtp) + append_history(p, "RTCPvideo", "Quality:%s", videoqos); + if (p->trtp) + append_history(p, "RTCPtext", "Quality:%s", textqos); + } + if (p->rtp && oldowner) + pbx_builtin_setvar_helper(oldowner, "RTPAUDIOQOS", audioqos); + if (p->vrtp && oldowner) + pbx_builtin_setvar_helper(oldowner, "RTPVIDEOQOS", videoqos); + if (p->trtp && oldowner) + pbx_builtin_setvar_helper(oldowner, "RTPTEXTQOS", textqos); + } else { + /* Note we will need a BYE when this all settles out + but we can't send one while we have "INVITE" outstanding. */ + ast_set_flag(&p->flags[0], SIP_PENDINGBYE); + ast_clear_flag(&p->flags[0], SIP_NEEDREINVITE); + AST_SCHED_DEL_UNREF(sched, p->waitid, dialog_unref(p, "when you delete the waitid sched, you should dec the refcount for the stored dialog ptr")); + if (sip_cancel_destroy(p)) + ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n"); + } + } + } + if (needdestroy) { + pvt_set_needdestroy(p, "hangup"); + } + sip_pvt_unlock(p); + p = p->next; + } while (p); + return 0; } @@ -11104,14 +11143,67 @@ { int realtimeregs = ast_check_realtime("sipregs"); char *tablename = (realtimeregs) ? "sipregs" : "sippeers"; + struct sip_peer *tmppeer = NULL; + struct sip_peer *prevpeer = NULL; + struct sip_peer *headpeer = NULL; + int flag = 0, count = 0; + char tmpdata[SIPBUFSIZE] = {0}; + char dbdata[SIPBUFSIZE] = {0}; + char *tmpstr = NULL; + char *scan = NULL; - if (!sip_cfg.ignore_regexpire) { - if (peer->rt_fromcontact) { - ast_update_realtime(tablename, "name", peer->name, "fullcontact", "", "ipaddr", "", "port", "", "regseconds", "0", peer->deprecated_username ? "username" : "defaultuser", "", "regserver", "", "useragent", "", SENTINEL); - ast_update_realtime("sippeers", "name", peer->name, "lastms", "", NULL); - } else { - ast_db_del("SIP/Registry", peer->name); - } + headpeer = find_peer(peer->name, NULL, TRUE, FINDALLDEVICES, FALSE); + if(!headpeer) { + ast_log(LOG_NOTICE, "peer not found, this is an error\n"); + return; + } + else + count = ast_strlen_zero(headpeer->fullcontact); + + tmppeer = prevpeer = headpeer; + + if(ast_db_get("SIP/Registry", peer->name, dbdata, sizeof(dbdata))) + ast_log(LOG_WARNING, "Failed to open database\n"); + + scan = dbdata; + + /* copy the entries in dbdata except the one for which is being destroyed */ + while(tmppeer) { + if(tmppeer != peer) { + if(!count) { + tmpstr = strsep(&scan, ","); + strlcat(tmpdata, tmpstr, sizeof(tmpdata)); + strlcat(tmpdata, ",", sizeof(tmpdata)); + } + else + count = 0; + } + else { + if(!count) + tmpstr = strsep(&scan, ","); + else + count = 0; + prevpeer->list.next = tmppeer->list.next; /*happens only once */ + } + + prevpeer = tmppeer; + tmppeer = tmppeer->list.next; + } + unref_peer(headpeer, "unref peer"); + + flag = ast_strlen_zero(tmpdata); + + if(!flag) { + ast_db_del("SIP/Registry", peer->name); /*delete old entry */ + ast_db_put("SIP/Registry", peer->name, tmpdata); /*update with new one */ + } + else { + if (!sip_cfg.ignore_regexpire) { + if (peer->rt_fromcontact) + ast_update_realtime(tablename, "name", peer->name, "fullcontact", "", "ipaddr", "", "port", "", "regseconds", "0", peer->deprecated_username ? "username" : "defaultuser", "", "regserver", "", "useragent", "", SENTINEL); + else + ast_db_del("SIP/Registry", peer->name); + } } } @@ -11119,19 +11211,25 @@ static int expire_register(const void *data) { struct sip_peer *peer = (struct sip_peer *)data; - + struct sip_peer *headpeer = NULL; + if (!peer) /* Hmmm. We have no peer. Weird. */ return 0; + headpeer = find_peer(peer->name, NULL, TRUE, FINDALLDEVICES, FALSE); + peer->expire = -1; memset(&peer->addr, 0, sizeof(peer->addr)); + + destroy_association(peer); /* remove registration data from storage */ - destroy_association(peer); /* remove registration data from storage */ - - manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: SIP\r\nPeer: SIP/%s\r\nPeerStatus: Unregistered\r\nCause: Expired\r\n", peer->name); - register_peer_exten(peer, FALSE); /* Remove regexten */ - ast_devstate_changed(AST_DEVICE_UNKNOWN, "SIP/%s", peer->name); + if(headpeer == peer && !peer->list.next) { /*unregister only if there is only one user registered as this peer */ + manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: SIP\r\nPeer: SIP/%s\r\nPeerStatus: Unregistered\r\nCause: Expired\r\n", peer->name); + register_peer_exten(peer, FALSE); /* Remove regexten */ + ast_devstate_changed(AST_DEVICE_UNKNOWN, "SIP/%s", peer->name); + } + unref_peer(headpeer, "unref tmppeer"); /* Do we need to release this peer from memory? Only for realtime peers and autocreated peers */ @@ -11166,65 +11264,79 @@ } /*! \brief Get registration details from Asterisk DB */ -static void reg_source_db(struct sip_peer *peer) +static void reg_source_db(struct sip_peer *peer, int flag) { char data[256]; struct in_addr in; int expire; int port; char *scan, *addr, *port_str, *expiry_str, *username, *contact; + struct sip_peer* headpeer = peer; if (peer->rt_fromcontact) return; if (ast_db_get("SIP/Registry", peer->name, data, sizeof(data))) return; + /* there can be multiple comma seperated entries for the same peer, put them as a list*/ scan = data; - addr = strsep(&scan, ":"); - port_str = strsep(&scan, ":"); - expiry_str = strsep(&scan, ":"); - username = strsep(&scan, ":"); - contact = scan; /* Contact include sip: and has to be the last part of the database entry as long as we use : as a separator */ + while(!ast_strlen_zero(scan)) { + addr = strsep(&scan, ":"); + port_str = strsep(&scan, ":"); + expiry_str = strsep(&scan, ":"); + username = strsep(&scan, ":"); + contact = strsep(&scan, ","); - if (!inet_aton(addr, &in)) - return; + if (!inet_aton(addr, &in)) + return; - if (port_str) - port = atoi(port_str); - else - return; + if (port_str) + port = atoi(port_str); + else + return; - if (expiry_str) - expire = atoi(expiry_str); - else - return; + if (expiry_str) + expire = atoi(expiry_str); + else + return; - if (username) - ast_string_field_set(peer, username, username); - if (contact) - ast_string_field_set(peer, fullcontact, contact); + if (username) + ast_string_field_set(peer, username, username); + if (contact) + ast_string_field_set(peer, fullcontact, contact); - ast_debug(2, "SIP Seeding peer from astdb: '%s' at %s@%s:%d for %d\n", - peer->name, peer->username, ast_inet_ntoa(in), port, expire); + ast_debug(2, "SIP Seeding peer from astdb: '%s' at %s@%s:%d for %d\n", + peer->name, peer->username, ast_inet_ntoa(in), port, expire); - memset(&peer->addr, 0, sizeof(peer->addr)); - peer->addr.sin_family = AF_INET; - peer->addr.sin_addr = in; - peer->addr.sin_port = htons(port); - if (sipsock < 0) { - /* SIP isn't up yet, so schedule a poke only, pretty soon */ - AST_SCHED_REPLACE_UNREF(peer->pokeexpire, sched, ast_random() % 5000 + 1, sip_poke_peer_s, peer, - unref_peer(_data, "removing poke peer ref"), - unref_peer(peer, "removing poke peer ref"), - ref_peer(peer, "adding poke peer ref")); - } else { - sip_poke_peer(peer, 0); - } - AST_SCHED_REPLACE_UNREF(peer->expire, sched, (expire + 10) * 1000, expire_register, peer, - unref_peer(_data, "remove registration ref"), - unref_peer(peer, "remove registration ref"), - ref_peer(peer, "add registration ref")); - register_peer_exten(peer, TRUE); + memset(&peer->addr, 0, sizeof(peer->addr)); + peer->addr.sin_family = AF_INET; + peer->addr.sin_addr = in; + peer->addr.sin_port = htons(port); + + AST_SCHED_REPLACE_UNREF(peer->expire, sched, (expire + 10) * 1000, expire_register, peer, + unref_peer(_data, "remove registration ref"), + unref_peer(peer, "remove registration ref"), + ref_peer(peer, "add registration ref")); + + if(peer->list.next || flag) + break; + else if(!ast_strlen_zero(scan) && !flag) { /*not called from temp_peer */ + peer->list.next = sip_allocpeer(peer->name); /*if there are multiple entries, make a list */ + peer = peer->list.next; + } + } + + if (sipsock < 0) { + /* SIP isn't up yet, so schedule a poke only, pretty soon */ + AST_SCHED_REPLACE_UNREF(headpeer->pokeexpire, sched, ast_random() % 5000 + 1, sip_poke_peer_s, headpeer, + unref_peer(_data, "removing poke peer ref"), + unref_peer(headpeer, "removing poke peer ref"), + ref_peer(headpeer, "adding poke peer ref")); + } else { + sip_poke_peer(headpeer, 0); + } + if(!flag) + register_peer_exten(headpeer, TRUE); } /*! \brief Save contact header for 200 OK on INVITE */ @@ -11336,7 +11448,11 @@ struct hostent *hp; struct ast_hostent ahp; struct sockaddr_in oldsin, testsin; - + struct sip_peer *prevpeer = NULL; + struct sip_peer *nextpeer = NULL; + struct sip_peer *tmppeer = NULL; + int nodepos = -1; + ast_copy_string(contact, get_header(req, "Contact"), sizeof(contact)); if (ast_strlen_zero(expires)) { /* No expires header, try look in Contact: */ @@ -11373,29 +11489,124 @@ return PARSE_REGISTER_QUERY; } else if (!strcasecmp(curi, "*") || !expire) { /* Unregister this peer */ /* This means remove all registrations and return OK */ - memset(&peer->addr, 0, sizeof(peer->addr)); + int removeall = 0; + int freepeer = 0; - AST_SCHED_DEL_UNREF(sched, peer->expire, - unref_peer(peer, "remove register expire ref")); + if(!strcasecmp(curi, "*")) + removeall = 1; + + tmppeer = prevpeer = peer; + do { + if(!removeall) { + while(tmppeer) { + /* if callid matches or the contact matches, this is the required peer, update*/ + if(!strcmp(tmppeer->callid, pvt->callid) || (!ast_strlen_zero(curi) && !strcmp(tmppeer->fullcontact, curi))) { + /*TODO: if peer is not the head node, not freeing head node because of some problems faced + * will have to free head node, and make the next in list as head*/ + if(tmppeer != peer) + freepeer = 1; + break; + } + prevpeer = tmppeer; + tmppeer = tmppeer->list.next; + } + if(!tmppeer) { + ast_log(LOG_NOTICE, "received unregister request for the peer not registered\n"); + return PARSE_REGISTER_UPDATE; + } + } + + memset(&tmppeer->addr, 0, sizeof(tmppeer->addr)); - destroy_association(peer); - - register_peer_exten(peer, FALSE); /* Remove extension from regexten= setting in sip.conf */ - ast_string_field_set(peer, fullcontact, ""); - ast_string_field_set(peer, useragent, ""); - peer->sipoptions = 0; - peer->lastms = 0; - pvt->expiry = 0; + AST_SCHED_DEL_UNREF(sched, tmppeer->expire, + unref_peer(tmppeer, "remove register expire ref")); - ast_verb(3, "Unregistered SIP '%s'\n", peer->name); + destroy_association(tmppeer); - manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: SIP\r\nPeer: SIP/%s\r\nPeerStatus: Unregistered\r\n", peer->name); - return PARSE_REGISTER_UPDATE; + if((!peer->list.next && freepeer != 1) || (!peer->list.next && ast_strlen_zero(peer->fullcontact))) { + register_peer_exten(tmppeer, FALSE); /* Remove extension from regexten= setting in sip.conf */ + } + + ast_string_field_set(tmppeer, fullcontact, ""); + ast_string_field_set(tmppeer, useragent, ""); + tmppeer->sipoptions = 0; + tmppeer->lastms = 0; + pvt->expiry = 0; + + ast_verb(3, "Unregistered SIP '%s'\n", tmppeer->name); + + manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: SIP\r\nPeer: SIP/%s\r\nPeerStatus: Unregistered\r\n", tmppeer->name); + /* if * received in contact, remove all the nodes in the list */ + if(removeall) { + if(tmppeer != peer) { + prevpeer->list.next = tmppeer->list.next; + ao2_t_unlink(peers, tmppeer, "ao2_unlink of peer from peer table"); + ao2_t_unlink(peers_by_ip, tmppeer, "ao2_unlink of peer from peers_by_ip table"); + ast_free(tmppeer); + } + tmppeer = prevpeer->list.next; + } + + if(!tmppeer) + break; + } while(removeall); + + if(freepeer) { + ao2_t_unlink(peers, tmppeer, "ao2_unlink of peer from peer table"); + ao2_t_unlink(peers_by_ip, tmppeer, "ao2_unlink of peer from peers_by_ip table"); + ast_free(tmppeer); + } + return PARSE_REGISTER_UPDATE; } - + /* Store whatever we got as a contact from the client */ - ast_string_field_set(peer, fullcontact, curi); + do { + tmppeer = prevpeer = peer; + if(ast_strlen_zero(peer->fullcontact)) + nodepos--; + + /* Registration refreshes */ + while(tmppeer) { + nodepos++; + /* if allowforking=no or matches with callid or contact */ + if(!sip_cfg.allow_forking || !strcmp(pvt->callid, tmppeer->callid) || (!ast_strlen_zero(curi) && !strcmp(tmppeer->fullcontact, curi))) + { + nextpeer = tmppeer; + goto contactset; + } + + prevpeer = tmppeer; + tmppeer = tmppeer->list.next; + } + + nodepos = -1; + + /*if first peer contact is null, we have to fill it first */ + if(ast_strlen_zero(peer->fullcontact)) { + nextpeer = peer; + goto contactset; + } + + nextpeer = temp_peer(peer->name); + + if (nextpeer) { + ao2_t_link(peers, nextpeer, "link peer into peer table"); + if (nextpeer->addr.sin_addr.s_addr) { + ao2_t_link(peers_by_ip, nextpeer, "link peer into peers-by-ip table"); + } + } + else + return PARSE_REGISTER_FAILED; + + prevpeer->list.next = nextpeer; + } while (0); + +contactset: + ast_string_field_set(nextpeer, fullcontact, curi); + ast_string_field_set(nextpeer, callid, pvt->callid); + + peer = nextpeer; /* For the 200 OK, we should use the received contact */ ast_string_field_build(pvt, our_contact, "<%s>", curi); @@ -11482,7 +11693,9 @@ } } pvt->expiry = expire; - snprintf(data, sizeof(data), "%s:%d:%d:%s:%s", ast_inet_ntoa(peer->addr.sin_addr), ntohs(peer->addr.sin_port), expire, peer->username, peer->fullcontact); + + /* add peer info to db */ + sip_db_add(peer, expire, data, nodepos); /* Saving TCP connections is useless, we won't be able to reconnect XXX WHY???? XXX \todo Fix this immediately. @@ -15263,25 +15476,25 @@ /*! \brief Do completion on registered peer name */ static char *complete_sip_registered_peer(const char *word, int state, int flags2) { - char *result = NULL; - int wordlen = strlen(word); - int which = 0; - struct ao2_iterator i; - struct sip_peer *peer; + char *result = NULL; + int wordlen = strlen(word); + int which = 0; + struct ao2_iterator i; + struct sip_peer *peer; - i = ao2_iterator_init(peers, 0); - while ((peer = ao2_t_iterator_next(&i, "iterate thru peers table"))) { - if (!strncasecmp(word, peer->name, wordlen) && - (!flags2 || ast_test_flag(&peer->flags[1], flags2)) && + i = ao2_iterator_init(peers, 0); + while ((peer = ao2_t_iterator_next(&i, "iterate thru peers table"))) { + if (!strncasecmp(word, peer->name, wordlen) && + (!flags2 || ast_test_flag(&peer->flags[1], flags2)) && ++which > state && peer->expire > 0) result = ast_strdup(peer->name); - if (result) { - unref_peer(peer, "toss iterator peer ptr before break"); - break; - } - unref_peer(peer, "toss iterator peer ptr"); - } - return result; + if (result) { + unref_peer(peer, "toss iterator peer ptr before break"); + break; + } + unref_peer(peer, "toss iterator peer ptr"); + } + return result; } /*! \brief Support routine for 'sip show history' CLI */ @@ -16505,17 +16718,32 @@ struct ast_channel *bridgepeer = NULL; char *p_hdrval; int rtn; - + int hangup = 0; + struct sip_pvt *headp = NULL; + struct sip_pvt *tmpp = NULL; + if (reinvite) + { + ast_log(LOG_NOTICE, "SIP response %d to RE-invite on %s call %s\n", resp, outgoing ? "outgoing" : "incoming", p->callid); ast_debug(4, "SIP response %d to RE-invite on %s call %s\n", resp, outgoing ? "outgoing" : "incoming", p->callid); + } else + { + ast_log(LOG_NOTICE, "SIP response %d to standard invite\n", resp); ast_debug(4, "SIP response %d to standard invite\n", resp); + } if (p->alreadygone) { /* This call is already gone */ ast_debug(1, "Got response on call that is already terminated: %s (ignoring)\n", p->callid); return; } + if(p->owner) { + headp = p->owner->tech_pvt; + if(p->owner->no_of_pvt_channels < 1) + hangup = 1; + } + /* Acknowledge sequence number - This only happens on INVITE from SIP-call */ /* Don't auto congest anymore since we've gotten something useful back */ AST_SCHED_DEL_UNREF(sched, p->initid, dialog_unref(p, "when you delete the initid sched, you should dec the refcount for the stored dialog ptr")); @@ -16618,7 +16846,7 @@ if (p->owner && (p->owner->_state == AST_STATE_UP) && (bridgepeer = ast_bridged_channel(p->owner))) { /* if this is a re-invite */ struct sip_pvt *bridgepvt = NULL; - if (!bridgepeer->tech) { + if (!bridgepeer->tech) { ast_log(LOG_WARNING, "Ooooh.. no tech! That's REALLY bad\n"); break; } @@ -16652,6 +16880,15 @@ if (!req->ignore && p->owner) { if (!reinvite) { + if (ast_test_flag(&p->flags[0], SIP_OUTGOING)) { + /* if tech_pvt ref in owner is not the one with which call is being established, + * make it the tech_pvt in owner, others are anyhow going to die */ + if(p->owner->tech_pvt != p) + { + p->owner->tech_pvt = dialog_unref(p->owner->tech_pvt, "unref p->owner->tech_pvt"); + p->owner->tech_pvt = dialog_ref(p, "set p->owner->tech_pvt to p"); + } + } ast_queue_control(p->owner, AST_CONTROL_ANSWER); if (sip_cfg.callevents) manager_event(EVENT_FLAG_SYSTEM, "ChannelUpdate", @@ -16699,12 +16936,38 @@ } } - /* If I understand this right, the branch is different for a non-200 ACK only */ p->invitestate = INV_TERMINATED; ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED); xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, TRUE); check_pendings(p); + + if (ast_test_flag(&p->flags[0], SIP_OUTGOING)) { + tmpp = headp; + /* send CANCEL or BYE to other forked calls */ + while(tmpp) { + if(tmpp != p) { + /* stop retransmitting an INVITE that has not received a response */ + __sip_pretend_ack(tmpp); + + /* if we can't send right now, mark it pending */ + if (tmpp->invitestate != INV_TERMINATED && tmpp->owner != NULL) { + ast_set_flag(&tmpp->flags[0], SIP_PENDINGBYE); + tmpp->pendinginvite = 0; + tmpp->owner = NULL; + stop_media_flows(tmpp); + } + if ( tmpp->initid != -1 ) { + update_call_counter(tmpp, INC_CALL_LIMIT); + } + check_pendings(tmpp); + } + p->owner->no_of_pvt_channels--; + tmpp = tmpp->next; + } + p->next = NULL; + } + break; case 407: /* Proxy authentication */ @@ -16721,6 +16984,26 @@ p->invitestate = INV_CALLING; if (p->authtries == MAX_AUTHTRIES || do_proxy_auth(p, req, resp, SIP_INVITE, 1)) { ast_log(LOG_NOTICE, "Failed to authenticate on INVITE to '%s'\n", get_header(&p->initreq, "From")); + if(headp == p) { + struct ast_channel* owner = headp->owner; + owner->no_of_pvt_channels--; + headp = p->next; + /* this sip_pvt is going to die, make other in the list as the ref to owner */ + owner->tech_pvt = dialog_unref(p, "unref p"); + owner->tech_pvt = dialog_ref(headp, "ref headp"); + } + else { + tmpp = p->owner->tech_pvt; + while(tmpp) { + if(tmpp->next == p) { + tmpp->next = p->next; + p->owner->no_of_pvt_channels--; + break; + } + tmpp = tmpp->next; + } + } + pvt_set_needdestroy(p, "failed to authenticate on INVITE"); sip_alreadygone(p); if (p->owner) @@ -16733,7 +17016,7 @@ /* First we ACK */ xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE); ast_log(LOG_WARNING, "Received response: \"Forbidden\" from '%s'\n", get_header(&p->initreq, "From")); - if (!req->ignore && p->owner) + if (!req->ignore && p->owner && hangup) ast_queue_control(p->owner, AST_CONTROL_CONGESTION); pvt_set_needdestroy(p, "received 403 response"); sip_alreadygone(p); @@ -16741,7 +17024,7 @@ case 404: /* Not found */ xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE); - if (p->owner && !req->ignore) + if (p->owner && !req->ignore && hangup) ast_queue_control(p->owner, AST_CONTROL_CONGESTION); sip_alreadygone(p); break; @@ -16751,7 +17034,7 @@ /* Could be REFER caused INVITE with replaces */ ast_log(LOG_WARNING, "Re-invite to non-existing call leg on other UA. SIP dialog '%s'. Giving up.\n", p->callid); xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE); - if (p->owner) + if (p->owner && hangup) ast_queue_control(p->owner, AST_CONTROL_CONGESTION); sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); break; @@ -16760,26 +17043,27 @@ xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE); ast_string_field_set(p, theirtag, NULL); proc_422_rsp(p, req); + if(p->owner) + p->owner->no_of_pvt_channels++; break; case 428: /* Use identity header - rfc 4474 - not supported by Asterisk yet */ xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE); append_history(p, "Identity", "SIP identity is required. Not supported by Asterisk."); ast_log(LOG_WARNING, "SIP identity required by proxy. SIP dialog '%s'. Giving up.\n", p->callid); - if (p->owner) + if (p->owner && hangup) ast_queue_control(p->owner, AST_CONTROL_CONGESTION); break; - - case 487: /* Cancelled transaction */ /* We have sent CANCEL on an outbound INVITE This transaction is already scheduled to be killed by sip_hangup(). */ xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE); - if (p->owner && !req->ignore) { - ast_queue_hangup_with_cause(p->owner, AST_CAUSE_NORMAL_CLEARING); - append_history(p, "Hangup", "Got 487 on CANCEL request from us. Queued AST hangup request"); + + if (p->owner && !req->ignore && hangup) { + ast_queue_hangup_with_cause(p->owner, AST_CAUSE_NORMAL_CLEARING); + append_history(p, "Hangup", "Got 487 on CANCEL request from us. Queued AST hangup request"); } else if (!req->ignore) { update_call_counter(p, DEC_CALL_LIMIT); append_history(p, "Hangup", "Got 487 on CANCEL request from us on call without owner. Killing this dialog."); @@ -16796,6 +17080,8 @@ /* Trigger a reinvite back to audio */ transmit_reinvite_with_sdp(p, FALSE, FALSE); + if(p->owner) + p->owner->no_of_pvt_channels++; } else if (p->udptl && p->t38.state == T38_LOCAL_DIRECT) { /* We tried to send T.38 out in an initial INVITE and the remote side rejected it, right now we can't fall back to audio so totally abort. @@ -16806,13 +17092,13 @@ change_t38_state(p, T38_DISABLED); /* The dialog is now terminated */ - if (p->owner && !req->ignore) + if (p->owner && !req->ignore && hangup) ast_queue_control(p->owner, AST_CONTROL_CONGESTION); pvt_set_needdestroy(p, "got error on T.38 initial invite"); sip_alreadygone(p); } else { /* We can't set up this call, so give up */ - if (p->owner && !req->ignore) + if (p->owner && !req->ignore && hangup) ast_queue_control(p->owner, AST_CONTROL_CONGESTION); pvt_set_needdestroy(p, "received 488 response"); /* If there's no dialog to end, then mark p as already gone */ @@ -16823,7 +17109,7 @@ case 491: /* Pending */ xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE); if (p->owner && !req->ignore) { - if (p->owner->_state != AST_STATE_UP) { + if (p->owner->_state != AST_STATE_UP && hangup) { ast_queue_control(p->owner, AST_CONTROL_CONGESTION); pvt_set_needdestroy(p, "received 491 response"); } else { @@ -16840,7 +17126,7 @@ case 501: /* Not implemented */ xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE); - if (p->owner) + if (p->owner && hangup) ast_queue_control(p->owner, AST_CONTROL_CONGESTION); break; } @@ -17240,6 +17526,8 @@ char *c_copy = ast_strdupa(c); /* Skip the Cseq and its subsequent spaces */ const char *msg = ast_skip_blanks(ast_skip_nonblanks(c_copy)); + int hangup = 0; + struct sip_pvt *tmpp = NULL; if (!msg) msg = ""; @@ -17247,9 +17535,38 @@ sipmethod = find_sip_method(msg); owner = p->owner; - if (owner) + if (owner) { + if(resp > 299 && resp != 401 && resp != 407 && ast_test_flag(&p->flags[0], SIP_OUTGOING)) { + if(owner->tech_pvt == p) { + /* decrementing no of pvt channels in owner as we got error response */ + owner->no_of_pvt_channels--; + p->peer = NULL; + if(p->next) + { + tmpp = p->next; + /*make some others in the list as the ref to owner */ + owner->tech_pvt = dialog_unref(p, "unref p"); + owner->tech_pvt = dialog_ref(tmpp, "ref p"); + } + } + else { + tmpp = owner->tech_pvt; + while(tmpp) { + if(tmpp->next == p) { + tmpp->next = p->next; + p->peer = NULL; + owner->no_of_pvt_channels--; + break; + } + tmpp = tmpp->next; + } + } + } owner->hangupcause = hangup_sip2cause(resp); - + if(owner->no_of_pvt_channels < 1) + hangup = 1; + } + /* Acknowledge whatever it is destined for */ if ((resp >= 100) && (resp <= 199)) __sip_semi_ack(p, seqno, 0, sipmethod); @@ -17472,7 +17789,7 @@ case 486: /* Busy here */ case 600: /* Busy everywhere */ case 603: /* Decline */ - if (p->owner) + if (p->owner && hangup) ast_queue_control(p->owner, AST_CONTROL_BUSY); break; case 482: /*! @@ -17501,18 +17818,21 @@ case 502: /* Bad gateway */ case 503: /* Service Unavailable */ case 504: /* Server Timeout */ - if (owner) + if (owner && hangup) ast_queue_control(p->owner, AST_CONTROL_CONGESTION); break; default: /* Send hangup */ - if (owner && sipmethod != SIP_MESSAGE && sipmethod != SIP_INFO && sipmethod != SIP_BYE) + if (owner && hangup && sipmethod != SIP_MESSAGE && sipmethod != SIP_INFO && sipmethod != SIP_BYE) ast_queue_hangup_with_cause(p->owner, AST_CAUSE_PROTOCOL_ERROR); break; } /* ACK on invite */ - if (sipmethod == SIP_INVITE) + if (sipmethod == SIP_INVITE) { transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE); + p->pendinginvite = 0; + pvt_set_needdestroy(p, "transaction completed"); + } if (sipmethod != SIP_MESSAGE && sipmethod != SIP_INFO) sip_alreadygone(p); if (!p->owner) { @@ -18897,7 +19217,7 @@ ast_string_field_set(p, theirtag, NULL); return 0; } - + /* We have a succesful authentication, process the SDP portion if there is one */ if (find_sdp(req)) { if (process_sdp(p, req, SDP_T38_INITIATE)) { @@ -19776,7 +20096,6 @@ /*! \brief Handle incoming CANCEL request */ static int handle_request_cancel(struct sip_pvt *p, struct sip_request *req) { - check_via(p, req); sip_alreadygone(p); @@ -20899,7 +21218,11 @@ if (p->owner && !nounlock) ast_channel_unlock(p->owner); - sip_pvt_unlock(p); + + if(p->peer == NULL && ast_test_flag(&p->flags[0], SIP_OUTGOING)) + p->owner = NULL; + + sip_pvt_unlock(p); ast_mutex_unlock(&netlock); ao2_t_ref(p, -1, "throw away dialog ptr from find_call at end of routine"); /* p is gone after the return */ return 1; @@ -21875,7 +22198,7 @@ */ static struct ast_channel *sip_request_call(const char *type, int format, void *data, int *cause) { - struct sip_pvt *p; + struct sip_pvt *p = NULL; struct ast_channel *tmpc = NULL; char *ext = NULL, *host; char tmp[256]; @@ -21887,6 +22210,11 @@ char *trans = NULL; enum sip_transport transport = 0; int oldformat = format; + struct sip_peer *peer = NULL; + struct sip_peer *tmppeer = NULL; + struct sip_pvt *tmpp = NULL; + struct sip_pvt *headp = NULL; + int pvt_channels = 0; /* mask request with some set of allowed formats. * XXX this needs to be fixed. @@ -21903,122 +22231,161 @@ } ast_debug(1, "Asked to create a SIP channel with formats: %s\n", ast_getformatname_multiple(tmp, sizeof(tmp), oldformat)); - if (!(p = sip_alloc(NULL, NULL, 0, SIP_INVITE))) { - ast_log(LOG_ERROR, "Unable to build sip pvt data for '%s' (Out of memory or socket error)\n", dest); - *cause = AST_CAUSE_SWITCH_CONGESTION; + peer = find_peer(dest, NULL, TRUE, FINDALLDEVICES, FALSE); + + if(!peer) { + *cause = AST_CAUSE_UNREGISTERED; + unref_peer(peer, "unref_peer: from sip_request_call"); return NULL; - } + } + + tmppeer = peer; + + if(ast_strlen_zero(tmppeer->fullcontact)) + tmppeer = tmppeer->list.next; - p->outgoing_call = TRUE; + if(!tmppeer) { + *cause = AST_CAUSE_UNREGISTERED; + ast_debug(3, "Cant create SIP call - target device not registred\n"); + unref_peer(peer, "unref_peer: from sip_request_call"); + return NULL; + } - if (!(p->options = ast_calloc(1, sizeof(*p->options)))) { - dialog_unlink_all(p, TRUE, TRUE); - dialog_unref(p, "unref dialog p from mem fail"); - /* sip_destroy(p); */ - ast_log(LOG_ERROR, "Unable to build option SIP data structure - Out of memory\n"); - *cause = AST_CAUSE_SWITCH_CONGESTION; - return NULL; - } + /* fork the call to all the locations this user has registerd from */ + do { + tmpp = p; + if (!(p = sip_alloc(NULL, NULL, 0, SIP_INVITE))) { + ast_log(LOG_ERROR, "Unable to build sip pvt data for '%s' (Out of memory or socket error)\n", dest); + *cause = AST_CAUSE_SWITCH_CONGESTION; + unref_peer(peer, "unref_peer: from sip_request_call"); + return NULL; + } + + p->outgoing_call = TRUE; + p->peer = tmppeer; - /* Save the destination, the SIP dial string */ - ast_copy_string(tmp, dest, sizeof(tmp)); + if (!(p->options = ast_calloc(1, sizeof(*p->options)))) { + dialog_unlink_all(p, TRUE, TRUE); + dialog_unref(p, "unref dialog p from mem fail"); + /* sip_destroy(p); */ + ast_log(LOG_ERROR, "Unable to build option SIP data structure - Out of memory\n"); + *cause = AST_CAUSE_SWITCH_CONGESTION; + unref_peer(peer, "unref_peer: from sip_request_call"); + return NULL; + } + /* Save the destination, the SIP dial string */ + ast_copy_string(tmp, dest, sizeof(tmp)); - /* Find DNID and take it away */ - dnid = strchr(tmp, '!'); - if (dnid != NULL) { - *dnid++ = '\0'; - ast_string_field_set(p, todnid, dnid); - } - /* Find at sign - @ */ - host = strchr(tmp, '@'); - if (host) { - *host++ = '\0'; - ext = tmp; - secret = strchr(ext, ':'); - } - if (secret) { - *secret++ = '\0'; - md5secret = strchr(secret, ':'); - } - if (md5secret) { - *md5secret++ = '\0'; - authname = strchr(md5secret, ':'); - } - if (authname) { - *authname++ = '\0'; - trans = strchr(authname, ':'); - } - if (trans) { - *trans++ = '\0'; - if (!strcasecmp(trans, "tcp")) - transport = SIP_TRANSPORT_TCP; - else if (!strcasecmp(trans, "tls")) - transport = SIP_TRANSPORT_TLS; - else { - if (strcasecmp(trans, "udp")) - ast_log(LOG_WARNING, "'%s' is not a valid transport option to Dial() for SIP calls, using udp by default.\n", trans); - transport = SIP_TRANSPORT_UDP; - } - } else { /* use default */ - transport = SIP_TRANSPORT_UDP; - } + /* Find DNID and take it away */ + dnid = strchr(tmp, '!'); + if (dnid != NULL) { + *dnid++ = '\0'; + ast_string_field_set(p, todnid, dnid); + } - if (!host) { - ext = strchr(tmp, '/'); - if (ext) - *ext++ = '\0'; - host = tmp; - } + /* Find at sign - @ */ + host = strchr(tmp, '@'); + if (host) { + *host++ = '\0'; + ext = tmp; + secret = strchr(ext, ':'); + } + if (secret) { + *secret++ = '\0'; + md5secret = strchr(secret, ':'); + } + if (md5secret) { + *md5secret++ = '\0'; + authname = strchr(md5secret, ':'); + } + if (authname) { + *authname++ = '\0'; + trans = strchr(authname, ':'); + } + if (trans) { + *trans++ = '\0'; + if (!strcasecmp(trans, "tcp")) + transport = SIP_TRANSPORT_TCP; + else if (!strcasecmp(trans, "tls")) + transport = SIP_TRANSPORT_TLS; + else { + if (strcasecmp(trans, "udp")) + ast_log(LOG_WARNING, "'%s' is not a valid transport option to Dial() for SIP calls, using udp by default.\n", trans); + transport = SIP_TRANSPORT_UDP; + } + } else { /* use default */ + transport = SIP_TRANSPORT_UDP; + } - p->socket.fd = -1; - p->socket.type = transport; + if (!host) { + ext = strchr(tmp, '/'); + if (ext) + *ext++ = '\0'; + host = tmp; + } - /* We now have - host = peer name, DNS host name or DNS domain (for SRV) - ext = extension (user part of URI) - dnid = destination of the call (applies to the To: header) - */ - if (create_addr(p, host, NULL, 1)) { - *cause = AST_CAUSE_UNREGISTERED; - ast_debug(3, "Cant create SIP call - target device not registered\n"); - dialog_unlink_all(p, TRUE, TRUE); - dialog_unref(p, "unref dialog p UNREGISTERED"); - /* sip_destroy(p); */ - return NULL; - } - if (ast_strlen_zero(p->peername) && ext) - ast_string_field_set(p, peername, ext); - /* Recalculate our side, and recalculate Call ID */ - ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip); - build_via(p); - ao2_t_unlink(dialogs, p, "About to change the callid -- remove the old name"); - build_callid_pvt(p); - ao2_t_link(dialogs, p, "Linking in under new name"); + p->socket.fd = -1; + p->socket.type = transport; + + /* We now have + host = peer name, DNS host name or DNS domain (for SRV) + ext = extension (user part of URI) + dnid = destination of the call (applies to the To: header) + */ + if (create_addr(p, host, NULL, 1)) { + *cause = AST_CAUSE_UNREGISTERED; + ast_debug(3, "Cant create SIP call - target device not registered\n"); + dialog_unlink_all(p, TRUE, TRUE); + dialog_unref(p, "unref dialog p UNREGISTERED"); + unref_peer(peer, "unref_peer: from sip_request_call"); + /* sip_destroy(p); */ + return NULL; + } + if (ast_strlen_zero(p->peername) && ext) + ast_string_field_set(p, peername, ext); + /* Recalculate our side, and recalculate Call ID */ + ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip); + build_via(p); + ao2_t_unlink(dialogs, p, "About to change the callid -- remove the old name"); + build_callid_pvt(p); + ao2_t_link(dialogs, p, "Linking in under new name"); - /* We have an extension to call, don't use the full contact here */ - /* This to enable dialing registered peers with extension dialling, - like SIP/peername/extension - SIP/peername will still use the full contact - */ - if (ext) { - ast_string_field_set(p, username, ext); - ast_string_field_set(p, fullcontact, NULL); - } - if (secret && !ast_strlen_zero(secret)) - ast_string_field_set(p, peersecret, secret); + /* We have an extension to call, don't use the full contact here */ + /* This to enable dialing registered peers with extension dialling, + like SIP/peername/extension + SIP/peername will still use the full contact + */ + if (ext) { + ast_string_field_set(p, username, ext); + ast_string_field_set(p, fullcontact, NULL); + } + if (secret && !ast_strlen_zero(secret)) + ast_string_field_set(p, peersecret, secret); - if (md5secret && !ast_strlen_zero(md5secret)) - ast_string_field_set(p, peermd5secret, md5secret); + if (md5secret && !ast_strlen_zero(md5secret)) + ast_string_field_set(p, peermd5secret, md5secret); - if (authname && !ast_strlen_zero(authname)) - ast_string_field_set(p, authname, authname); + if (authname && !ast_strlen_zero(authname)) + ast_string_field_set(p, authname, authname); #if 0 printf("Setting up to call extension '%s' at '%s'\n", ext ? ext : "", host); #endif - p->prefcodec = oldformat; /* Format for this call */ - p->jointcapability = oldformat; + p->prefcodec = oldformat; /* Format for this call */ + p->jointcapability = oldformat; + + if(tmpp) + tmpp->next = p; + + if(!headp) + headp = p; + tmppeer = tmppeer->list.next; + } while(tmppeer); + + unref_peer(peer, "unref_peer: from sip_request_call"); + + p = headp; sip_pvt_lock(p); tmpc = sip_new(p, AST_STATE_DOWN, host); /* Place the call */ if (sip_cfg.callevents) @@ -22030,8 +22397,20 @@ dialog_unlink_all(p, TRUE, TRUE); /* sip_destroy(p); */ } - dialog_unref(p, "toss pvt ptr at end of sip_request_call"); - ast_update_use_count(); + + tmpp = headp; + + while(tmpp) { + dialog_unref(tmpp, "toss pvt ptr at end of sip_request_call"); + + pvt_channels++; + if(tmpp != headp) + tmpp->owner = tmpc; /* keep a ref to owner in pvt channels */ + ast_update_use_count(); + tmpp = tmpp->next; + } + + tmpc->no_of_pvt_channels = pvt_channels; /* update the no of forked channels*/ restart_monitor(); return tmpc; } @@ -22438,7 +22817,7 @@ peer->selfdestruct = TRUE; peer->host_dynamic = TRUE; peer->prefs = default_prefs; - reg_source_db(peer); + reg_source_db(peer, 1); return peer; } @@ -22484,7 +22863,7 @@ const char *srvlookup = NULL; static int deprecation_warning = 1; struct ast_str *fullcontact = ast_str_alloca(512); - + if (!realtime || ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS)) { /* Note we do NOT use find_peer here, to avoid realtime recursion */ /* We also use a case-sensitive comparison (unlike find_peer) so @@ -22924,7 +23303,7 @@ if (ast_test_flag(&peer->flags[1], SIP_PAGE2_ALLOWSUBSCRIBE)) sip_cfg.allowsubscribe = TRUE; /* No global ban any more */ if (!found && peer->host_dynamic && !peer->is_realtime) - reg_source_db(peer); + reg_source_db(peer, 0); /* If they didn't request that MWI is sent *only* on subscribe, go ahead and * subscribe to it now. */ @@ -23630,7 +24009,9 @@ ast_log(LOG_WARNING, "Invalid pokepeers '%s' at line %d of %s\n", v->value, v->lineno, config); global_qualify_peers = DEFAULT_QUALIFY_PEERS; } - } + } else if(!strcasecmp(v->name, "allowforking")) { + sip_cfg.allow_forking = ast_true(v->value) ? 1 : 0; + } } if (!sip_cfg.allow_external_domains && AST_LIST_EMPTY(&domain_list)) { @@ -23665,10 +24046,14 @@ if (ast_true(hassip) || (!hassip && genhassip)) { peer = build_peer(cat, gen, ast_variable_browse(ucfg, cat), 0, FALSE); if (peer) { - ao2_t_link(peers, peer, "link peer into peer table"); - if (peer->addr.sin_addr.s_addr) { - ao2_t_link(peers_by_ip, peer, "link peer into peers_by_ip table"); - } + struct sip_peer* tmppeer = peer; + while(tmppeer) { + ao2_t_link(peers, tmppeer, "link peer into peer table"); + if (tmppeer->addr.sin_addr.s_addr) { + ao2_t_link(peers_by_ip, tmppeer, "link peer into peers_by_ip table"); + } + tmppeer = tmppeer->list.next; + } unref_peer(peer, "unref_peer: from reload_config"); peer_count++; @@ -23729,10 +24114,14 @@ if (is_peer) { peer = build_peer(cat, ast_variable_browse(cfg, cat), NULL, 0, is_peer == 2); if (peer) { - ao2_t_link(peers, peer, "link peer into peers table"); - if (peer->addr.sin_addr.s_addr) { - ao2_t_link(peers_by_ip, peer, "link peer into peers_by_ip table"); - } + struct sip_peer* tmppeer = peer; + while(tmppeer) { + ao2_t_link(peers, tmppeer, "link peer into peers table"); + if (tmppeer->addr.sin_addr.s_addr) { + ao2_t_link(peers_by_ip, tmppeer, "link peer into peers_by_ip table"); + } + tmppeer = tmppeer->list.next; + } unref_peer(peer, "unref the result of the build_peer call. Now, the links from the tables are the only ones left."); peer_count++; } @@ -24717,6 +25106,107 @@ return 0; } +/* add peer info to db, there can be a user registered from multiple locations, so update all of them into db*/ +static int sip_db_add(struct sip_peer *peer, int expire, char* data, int nodepos) +{ + char tmpdata[SIPBUFSIZE] = {0}; + char tmpstr[SIPBUFSIZE] = {0}; + char *scan = NULL; + char *s = NULL; + + if (!ast_db_get("SIP/Registry", peer->name, data, SIPBUFSIZE)) { + scan = data; + if(nodepos < 0) { + while(scan) { + s = strsep(&scan, ","); + if(!ast_strlen_zero(s)) { + strlcat(tmpdata, s, sizeof(tmpdata)); + strlcat(tmpdata, ",", sizeof(tmpdata)); + } + } + + strlcat(tmpdata, ast_inet_ntoa(peer->addr.sin_addr), sizeof(tmpdata)); + strlcat(tmpdata, ":", sizeof(tmpdata)); + + snprintf(tmpstr, sizeof(tmpstr), "%d", ntohs(peer->addr.sin_port)); + strlcat(tmpdata, tmpstr, sizeof(tmpdata)); + strlcat(tmpdata, ":", sizeof(tmpdata)); + + snprintf(tmpstr, sizeof(tmpstr), "%d", expire); + strlcat(tmpdata, tmpstr, sizeof(tmpdata)); + strlcat(tmpdata, ":", sizeof(tmpdata)); + + strlcat(tmpdata, peer->username, sizeof(tmpdata)); + strlcat(tmpdata, ":", sizeof(tmpdata)); + + strlcat(tmpdata, peer->fullcontact, sizeof(tmpdata)); + strlcat(tmpdata, ",", sizeof(tmpdata)); + } + else { + int object = 0; + while(scan) { + s = strsep(&scan, ","); + if(ast_strlen_zero(s)) + continue; + if(object != nodepos) { + strlcat(tmpdata, s, sizeof(tmpdata)); + strlcat(tmpdata, ",", sizeof(tmpdata)); + } + else { + strlcat(tmpdata, ast_inet_ntoa(peer->addr.sin_addr), sizeof(tmpdata)); + strlcat(tmpdata, ":", sizeof(tmpdata)); + + snprintf(tmpstr, sizeof(tmpstr), "%d", ntohs(peer->addr.sin_port)); + strlcat(tmpdata, tmpstr, sizeof(tmpdata)); + strlcat(tmpdata, ":", sizeof(tmpdata)); + + snprintf(tmpstr, sizeof(tmpstr), "%d", expire); + strlcat(tmpdata, tmpstr, sizeof(tmpdata)); + strlcat(tmpdata, ":", sizeof(tmpdata)); + + strlcat(tmpdata, peer->username, sizeof(tmpdata)); + strlcat(tmpdata, ":", sizeof(tmpdata)); + + strlcat(tmpdata, peer->fullcontact, sizeof(tmpdata)); + strlcat(tmpdata, ",", sizeof(tmpdata)); + } + object++; + } + } + memset(data, 0, SIPBUFSIZE); + snprintf(data, SIPBUFSIZE, "%s", tmpdata); + } + else + snprintf(data, SIPBUFSIZE, "%s:%d:%d:%s:%s,", ast_inet_ntoa(peer->addr.sin_addr), ntohs(peer->addr.sin_port), expire, peer->username, peer->fullcontact); + + return 0; +} + +/* allocate a sip_peer object */ +static struct sip_peer *sip_allocpeer(const char *name) +{ + struct sip_peer *peer; + + if (!(peer = ao2_t_alloc(sizeof(*peer), sip_destroy_peer_fn, "allocate a peer struct"))) + return NULL; + + if (ast_string_field_init(peer, 512)) { + ao2_t_ref(peer, -1, "failed to string_field_init, drop peer"); + return NULL; + } + + ast_atomic_fetchadd_int(&apeerobjs, 1); + set_peer_defaults(peer); + + ast_copy_string(peer->name, name, sizeof(peer->name)); + + peer->selfdestruct = TRUE; + peer->host_dynamic = TRUE; + peer->prefs = default_prefs; + peer->list.next = NULL; + return peer; +} + AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Session Initiation Protocol (SIP)", .load = load_module, .unload = unload_module,