diff options
Diffstat (limited to 'channels/chan_sip.c')
-rw-r--r-- | channels/chan_sip.c | 369 |
1 files changed, 233 insertions, 136 deletions
diff --git a/channels/chan_sip.c b/channels/chan_sip.c index dc8e98713..2768b3a4c 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -948,13 +948,6 @@ static const struct cfsip_options { */ #define ALLOWED_METHODS "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO" -/*! \brief SIP Extensions we support - \note This should be generated based on the previous array - in combination with settings. - \todo We should not have "timer" if it's disabled in the configuration file. -*/ -#define SUPPORTED_EXTENSIONS "replaces, timer" - /*! \brief Standard SIP unsecure port for UDP and TCP from RFC 3261. DO NOT CHANGE THIS */ #define STANDARD_SIP_PORT 5060 /*! \brief Standard SIP TLS port from RFC 3261. DO NOT CHANGE THIS */ @@ -3104,6 +3097,32 @@ cleanup: return NULL; } +/* this func is used with ao2_callback to unlink/delete all marked + peers */ +static int peer_is_marked(void *peerobj, void *arg, int flags) +{ + struct sip_peer *peer = peerobj; + return peer->the_mark ? CMP_MATCH : 0; +} + + +/* \brief Unlink all marked peers from ao2 containers */ +static void unlink_marked_peers_from_tables(void) +{ + ao2_t_callback(peers, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, peer_is_marked, NULL, + "initiating callback to remove marked peers"); + ao2_t_callback(peers_by_ip, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, peer_is_marked, NULL, + "initiating callback to remove marked peers"); +} + +/* \brief Unlink single peer from all ao2 containers */ +static void unlink_peer_from_tables(struct sip_peer *peer) +{ + ao2_t_unlink(peers, peer, "ao2_unlink of peer from peers table"); + if (peer->addr.sin_addr.s_addr) { + ao2_t_unlink(peers_by_ip, peer, "ao2_unlink of peer from peers_by_ip table"); + } +} /*! * helper functions to unreference various types of objects. @@ -3754,34 +3773,34 @@ static int retrans_pkt(const void *data) struct sip_pkt *pkt = (struct sip_pkt *)data, *prev, *cur = NULL; int reschedule = DEFAULT_RETRANS; int xmitres = 0; - + /* Lock channel PVT */ sip_pvt_lock(pkt->owner); if (pkt->retrans < MAX_RETRANS) { pkt->retrans++; - if (!pkt->timer_t1) { /* Re-schedule using timer_a and timer_t1 */ + if (!pkt->timer_t1) { /* Re-schedule using timer_a and timer_t1 */ if (sipdebug) - ast_debug(4, "SIP TIMER: Not rescheduling id #%d:%s (Method %d) (No timer T1)\n", pkt->retransid, sip_methods[pkt->method].text, pkt->method); + ast_debug(4, "SIP TIMER: Not rescheduling id #%d:%s (Method %d) (No timer T1)\n", pkt->retransid, sip_methods[pkt->method].text, pkt->method); } else { - int siptimer_a; - - if (sipdebug) - ast_debug(4, "SIP TIMER: Rescheduling retransmission #%d (%d) %s - %d\n", pkt->retransid, pkt->retrans, sip_methods[pkt->method].text, pkt->method); - if (!pkt->timer_a) - pkt->timer_a = 2 ; - else - pkt->timer_a = 2 * pkt->timer_a; - - /* For non-invites, a maximum of 4 secs */ - siptimer_a = pkt->timer_t1 * pkt->timer_a; /* Double each time */ - if (pkt->method != SIP_INVITE && siptimer_a > 4000) - siptimer_a = 4000; - - /* Reschedule re-transmit */ + int siptimer_a; + + if (sipdebug) + ast_debug(4, "SIP TIMER: Rescheduling retransmission #%d (%d) %s - %d\n", pkt->retransid, pkt->retrans, sip_methods[pkt->method].text, pkt->method); + if (!pkt->timer_a) + pkt->timer_a = 2 ; + else + pkt->timer_a = 2 * pkt->timer_a; + + /* For non-invites, a maximum of 4 secs */ + siptimer_a = pkt->timer_t1 * pkt->timer_a; /* Double each time */ + if (pkt->method != SIP_INVITE && siptimer_a > 4000) + siptimer_a = 4000; + + /* Reschedule re-transmit */ reschedule = siptimer_a; - ast_debug(4, "** SIP timers: Rescheduling retransmission %d to %d ms (t1 %d ms (Retrans id #%d)) \n", pkt->retrans +1, siptimer_a, pkt->timer_t1, pkt->retransid); - } + ast_debug(4, "** SIP timers: Rescheduling retransmission %d to %d ms (t1 %d ms (Retrans id #%d)) \n", pkt->retrans +1, siptimer_a, pkt->timer_t1, pkt->retransid); + } if (sip_debug_test_pvt(pkt->owner)) { const struct sockaddr_in *dst = sip_real_dst(pkt->owner); @@ -3793,12 +3812,13 @@ static int retrans_pkt(const void *data) append_history(pkt->owner, "ReTx", "%d %s", reschedule, pkt->data->str); xmitres = __sip_xmit(pkt->owner, pkt->data, pkt->packetlen); - sip_pvt_unlock(pkt->owner); - if (xmitres == XMIT_ERROR) + if (xmitres == XMIT_ERROR) { ast_log(LOG_WARNING, "Network error on retransmit in dialog %s\n", pkt->owner->callid); - else + } else { + sip_pvt_unlock(pkt->owner); return reschedule; - } + } + } /* Too many retries */ if (pkt->owner && pkt->method != SIP_OPTIONS && xmitres == 0) { if (pkt->is_fatal || sipdebug) /* Tell us if it's critical or if we're debugging */ @@ -3808,13 +3828,13 @@ static int retrans_pkt(const void *data) } else if (pkt->method == SIP_OPTIONS && sipdebug) { ast_log(LOG_WARNING, "Cancelling retransmit of OPTIONs (call id %s) -- See doc/sip-retransmit.txt.\n", pkt->owner->callid); - } + } if (xmitres == XMIT_ERROR) { ast_log(LOG_WARNING, "Transmit error :: Cancelling transmission on Call ID %s\n", pkt->owner->callid); append_history(pkt->owner, "XmitErr", "%s", pkt->is_fatal ? "(Critical)" : "(Non-critical)"); - } else + } else { append_history(pkt->owner, "MaxRetries", "%s", pkt->is_fatal ? "(Critical)" : "(Non-critical)"); - + } pkt->retransid = -1; if (pkt->is_fatal) { @@ -3824,9 +3844,9 @@ static int retrans_pkt(const void *data) sip_pvt_lock(pkt->owner); } - if (pkt->owner->owner && !pkt->owner->owner->hangupcause) + if (pkt->owner->owner && !pkt->owner->owner->hangupcause) pkt->owner->owner->hangupcause = AST_CAUSE_NO_USER_RESPONSE; - + if (pkt->owner->owner) { sip_alreadygone(pkt->owner); ast_log(LOG_WARNING, "Hanging up call %s - no reply to our critical packet (see doc/sip-retransmit.txt).\n", pkt->owner->callid); @@ -3846,7 +3866,7 @@ static int retrans_pkt(const void *data) if (pkt->method == SIP_BYE) { /* We're not getting answers on SIP BYE's. Tear down the call anyway. */ - if (pkt->owner->owner) + if (pkt->owner->owner) ast_channel_unlock(pkt->owner->owner); append_history(pkt->owner, "ByeFailure", "Remote peer doesn't respond to bye. Destroying call anyway."); pvt_set_needdestroy(pkt->owner, "no response to BYE"); @@ -3985,10 +4005,6 @@ static int __sip_autodestruct(const void *data) } } - if (p->subscribed == MWI_NOTIFICATION) - if (p->relatedpeer) - p->relatedpeer = unref_peer(p->relatedpeer, "__sip_autodestruct: unref peer p->relatedpeer"); /* Remove link to peer. If it's realtime, make sure it's gone from memory) */ - /* Reset schedule ID */ p->autokillid = -1; @@ -6090,7 +6106,6 @@ static int sip_hangup(struct ast_channel *ast) if (p->invitestate == INV_CALLING) { /* We can't send anything in CALLING state */ ast_set_flag(&p->flags[0], SIP_PENDINGBYE); - __sip_pretend_ack(p); /* Do we need a timer here if we don't hear from them at all? Yes we do or else we will get hung dialogs and those are no fun. */ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); append_history(p, "DELAY", "Not sending cancel, waiting for timeout"); @@ -8130,10 +8145,10 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action int vportno = -1; /*!< RTP Video port number */ int tportno = -1; /*!< RTP Text port number */ int udptlportno = -1; /*!< UDPTL Image port number */ - struct sockaddr_in sin; /*!< media socket address */ - struct sockaddr_in vsin; /*!< video socket address */ - struct sockaddr_in isin; /*!< image socket address */ - struct sockaddr_in tsin; /*!< text socket address */ + struct sockaddr_in sin = { 0, }; /*!< media socket address */ + struct sockaddr_in vsin = { 0, }; /*!< video socket address */ + struct sockaddr_in isin = { 0, }; /*!< image socket address */ + struct sockaddr_in tsin = { 0, }; /*!< text socket address */ /* Peer capability is the capability in the SDP, non codec is RFC2833 DTMF (101) */ int peercapability = 0, peernoncodeccapability = 0; @@ -8648,7 +8663,10 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action ast_set_write_format(p->owner, p->owner->writeformat); } - if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) && sin.sin_addr.s_addr && (!sendonly || sendonly == -1)) { + if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) + && (sin.sin_addr.s_addr || vsin.sin_addr.s_addr || + isin.sin_addr.s_addr || tsin.sin_addr.s_addr) + && (!sendonly || sendonly == -1)) { ast_queue_control(p->owner, AST_CONTROL_UNHOLD); /* Activate a re-invite */ ast_queue_frame(p->owner, &ast_null_frame); @@ -8664,7 +8682,9 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action if (sip_cfg.notifyhold) sip_peer_hold(p, FALSE); ast_clear_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD); /* Clear both flags */ - } else if (!sin.sin_addr.s_addr || (sendonly && sendonly != -1)) { + } else if (!(sin.sin_addr.s_addr || vsin.sin_addr.s_addr || + isin.sin_addr.s_addr || tsin.sin_addr.s_addr) + || (sendonly && sendonly != -1)) { int already_on_hold = ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD); ast_queue_control_data(p->owner, AST_CONTROL_HOLD, S_OR(p->mohsuggest, NULL), @@ -9096,6 +9116,20 @@ static void ts_ast_rtp_destroy(void *data) } #endif +/*! \brief Add "Supported" header to sip message. Since some options may + * be disabled in the config, the sip_pvt must be inspected to determine what + * is supported for this dialog. */ +static int add_supported_header(struct sip_pvt *pvt, struct sip_request *req) +{ + int res; + if (st_get_mode(pvt) != SESSION_TIMER_MODE_REFUSE) { + res = add_header(req, "Supported", "replaces, timer"); + } else { + res = add_header(req, "Supported", "replaces"); + } + return res; +} + /*! \brief Add header to SIP message */ static int add_header(struct sip_request *req, const char *var, const char *value) { @@ -9507,14 +9541,13 @@ static int respprep(struct sip_request *resp, struct sip_pvt *p, const char *msg if (!ast_strlen_zero(global_useragent)) add_header(resp, "Server", global_useragent); add_header(resp, "Allow", ALLOWED_METHODS); - add_header(resp, "Supported", SUPPORTED_EXTENSIONS); + add_supported_header(p, resp); /* If this is an invite, add Session-Timers related headers if the feature is active for this session */ if (p->method == SIP_INVITE && p->stimer && p->stimer->st_active == TRUE && p->stimer->st_active_peer_ua == TRUE) { char se_hdr[256]; snprintf(se_hdr, sizeof(se_hdr), "%d;refresher=%s", p->stimer->st_interval, strefresher2str(p->stimer->st_ref)); - add_header(resp, "Require", "timer"); add_header(resp, "Session-Expires", se_hdr); } @@ -10607,7 +10640,7 @@ static int transmit_reinvite_with_sdp(struct sip_pvt *p, int t38version, int old reqprep(&req, p, ast_test_flag(&p->flags[0], SIP_REINVITE_UPDATE) ? SIP_UPDATE : SIP_INVITE, 0, 1); add_header(&req, "Allow", ALLOWED_METHODS); - add_header(&req, "Supported", SUPPORTED_EXTENSIONS); + add_supported_header(p, &req); if (sipdebug) { if (oldsdp == TRUE) add_header(&req, "X-asterisk-Info", "SIP re-invite (Session-Timers)"); @@ -10983,7 +11016,7 @@ static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init) } add_header(&req, "Allow", ALLOWED_METHODS); - add_header(&req, "Supported", SUPPORTED_EXTENSIONS); + add_supported_header(p, &req); if(p->notify_headers) { char buf[512]; @@ -11315,27 +11348,36 @@ static int transmit_state_notify(struct sip_pvt *p, int state, int full, int tim int need = strlen(caller->cid.cid_num) + strlen(p->fromdomain) + sizeof("sip:@"); local_target = alloca(need); snprintf(local_target, need, "sip:%s@%s", caller->cid.cid_num, p->fromdomain); - local_display = ast_strdupa(caller->cid.cid_name); + if (!(ast_strlen_zero(caller->cid.cid_name))) { + local_display = ast_strdupa(caller->cid.cid_name); + } ast_channel_unlock(caller); caller = NULL; } + /* We create a fake call-id which the phone will send back in an INVITE + * Replaces header which we can grab and do some magic with. */ + if (sip_cfg.pedanticsipchecking) { + ast_str_append(&tmp, 0, "<dialog id=\"%s\" call-id=\"pickup-%s\" local-tag=\"%s\" remote-tag=\"%s\" direction=\"recipient\">\n", + p->exten, p->callid, p->theirtag, p->tag); + } else { + ast_str_append(&tmp, 0, "<dialog id=\"%s\" call-id=\"pickup-%s\" direction=\"recipient\">\n", + p->exten, p->callid); + } + ast_str_append(&tmp, 0, + "<remote>\n" + /* See the limitations of this above. Luckily the phone seems to still be + happy when these values are not correct. */ + "<identity display=\"%s\">%s</identity>\n" + "<target uri=\"%s\"/>\n" + "</remote>\n" + "<local>\n" + "<identity>%s</identity>\n" + "<target uri=\"%s\"/>\n" + "</local>\n", + local_display, local_target, local_target, mto, mto); + } else { + ast_str_append(&tmp, 0, "<dialog id=\"%s\" direction=\"recipient\">\n", p->exten); } - - /* We create a fake call-id which the phone will send back in an INVITE - Replaces header which we can grab and do some magic with. */ - ast_str_append(&tmp, 0, - "<dialog id=\"%s\" call-id=\"pickup-%s\" direction=\"recipient\">\n" - "<remote>\n" - /* See the limitations of this above. Luckily the phone seems to still be - happy when these values are not correct. */ - "<identity display=\"%s\">%s</identity>\n" - "<target uri=\"%s\"/>\n" - "</remote>\n" - "<local>\n" - "<identity>%s</identity>\n" - "<target uri=\"%s\"/>\n" - "</local>\n", - p->exten, p->callid, local_display, local_target, local_target, mto, mto); } else { ast_str_append(&tmp, 0, "<dialog id=\"%s\">\n", p->exten); } @@ -11424,7 +11466,7 @@ static int transmit_notify_with_sipfrag(struct sip_pvt *p, int cseq, char *messa add_header(&req, "Subscription-state", terminate ? "terminated;reason=noresource" : "active"); add_header(&req, "Content-Type", "message/sipfrag;version=2.0"); add_header(&req, "Allow", ALLOWED_METHODS); - add_header(&req, "Supported", SUPPORTED_EXTENSIONS); + add_supported_header(p, &req); snprintf(tmp, sizeof(tmp), "SIP/2.0 %s\r\n", message); add_content(&req, tmp); @@ -11976,7 +12018,7 @@ static int transmit_refer(struct sip_pvt *p, const char *dest) add_header(&req, "Refer-To", referto); add_header(&req, "Allow", ALLOWED_METHODS); - add_header(&req, "Supported", SUPPORTED_EXTENSIONS); + add_supported_header(p, &req); if (!ast_strlen_zero(p->our_contact)) add_header(&req, "Referred-By", p->our_contact); @@ -12114,7 +12156,6 @@ static int expire_register(const void *data) peer->expire = -1; peer->portinuri = 0; - memset(&peer->addr, 0, sizeof(peer->addr)); destroy_association(peer); /* remove registration data from storage */ set_socket_transport(&peer->socket, peer->default_outbound_transport); @@ -12136,12 +12177,13 @@ static int expire_register(const void *data) if (peer->selfdestruct || ast_test_flag(&peer->flags[1], SIP_PAGE2_RTAUTOCLEAR)) { - ao2_t_unlink(peers, peer, "ao2_unlink of peer from peers table"); - if (peer->addr.sin_addr.s_addr) { - ao2_t_unlink(peers_by_ip, peer, "ao2_unlink of peer from peers_by_ip table"); - } + unlink_peer_from_tables(peer); } + /* Only clear the addr after we check for destruction. The addr must remain + * in order to unlink from the peers_by_ip container correctly */ + memset(&peer->addr, 0, sizeof(peer->addr)); + unref_peer(peer, "removing peer ref for expire_register"); return 0; @@ -15131,14 +15173,6 @@ static int dialog_needdestroy(void *dialogobj, void *arg, int flags) return 0; } -/* this func is used with ao2_callback to unlink/delete all marked - peers */ -static int peer_is_marked(void *peerobj, void *arg, int flags) -{ - struct sip_peer *peer = peerobj; - return peer->the_mark ? CMP_MATCH : 0; -} - /*! \brief Remove temporary realtime objects from memory (CLI) */ /*! \todo XXXX Propably needs an overhaul after removal of the devices */ static char *sip_prune_realtime(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) @@ -15241,8 +15275,7 @@ static char *sip_prune_realtime(struct ast_cli_entry *e, int cmd, struct ast_cli } ao2_iterator_destroy(&i); if (pruned) { - ao2_t_callback(peers, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, peer_is_marked, NULL, - "initiating callback to remove marked peers"); + unlink_marked_peers_from_tables(); ast_cli(a->fd, "%d peers pruned.\n", pruned); } else ast_cli(a->fd, "No peers found to prune.\n"); @@ -17642,7 +17675,11 @@ static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req) } } -/*! \brief Check pending actions on SIP call */ +/*! \brief Check pending actions on SIP call + * + * \note both sip_pvt and sip_pvt's owner channel (if present) + * must be locked for this function. + */ static void check_pendings(struct sip_pvt *p) { if (ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) { @@ -17657,6 +17694,9 @@ static void check_pendings(struct sip_pvt *p) if (p->pendinginvite) return; + if (p->owner) { + ast_softhangup_nolock(p->owner, AST_SOFTHANGUP_DEV); + } /* Perhaps there is an SD change INVITE outstanding */ transmit_request_with_auth(p, SIP_BYE, 0, XMIT_RELIABLE, TRUE); } @@ -17682,12 +17722,21 @@ static void check_pendings(struct sip_pvt *p) static int sip_reinvite_retry(const void *data) { struct sip_pvt *p = (struct sip_pvt *) data; + struct ast_channel *owner; sip_pvt_lock(p); /* called from schedule thread which requires a lock */ + while ((owner = p->owner) && ast_channel_trylock(owner)) { + sip_pvt_unlock(p); + usleep(1); + sip_pvt_lock(p); + } ast_set_flag(&p->flags[0], SIP_NEEDREINVITE); p->waitid = -1; check_pendings(p); sip_pvt_unlock(p); + if (owner) { + ast_channel_unlock(owner); + } dialog_unref(p, "unref the dialog ptr from sip_reinvite_retry, because it held a dialog ptr"); return 0; } @@ -17840,7 +17889,7 @@ static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, stru /* Check for Session-Timers related headers */ if (st_get_mode(p) != SESSION_TIMER_MODE_REFUSE && p->outgoing_call == TRUE && !reinvite) { p_hdrval = (char*)get_header(req, "Session-Expires"); - if (!ast_strlen_zero(p_hdrval)) { + if (!ast_strlen_zero(p_hdrval)) { /* UAS supports Session-Timers */ enum st_refresher tmp_st_ref = SESSION_TIMER_REFRESHER_AUTO; int tmp_st_interval = 0; @@ -19970,7 +20019,7 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int struct sip_pvt *subscription = NULL; replace_id += 7; /* Worst case we are looking at \0 */ - if ((subscription = get_sip_pvt_byid_locked(replace_id, NULL, NULL)) == NULL) { + if ((subscription = get_sip_pvt_byid_locked(replace_id, totag, fromtag)) == NULL) { ast_log(LOG_NOTICE, "Unable to find subscription with call-id: %s\n", replace_id); transmit_response_reliable(p, "481 Call Leg Does Not Exist (Replaces)", req); error = 1; @@ -20215,10 +20264,10 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int } /* Session-Timers */ - if (p->sipoptions & SIP_OPT_TIMER) { + if ((p->sipoptions & SIP_OPT_TIMER) && !ast_strlen_zero(get_header(req, "Session-Expires"))) { /* The UAC has requested session-timers for this session. Negotiate the session refresh interval and who will be the refresher */ - ast_debug(2, "Incoming INVITE with 'timer' option enabled\n"); + ast_debug(2, "Incoming INVITE with 'timer' option supported and \"Session-Expires\" header.\n"); /* Allocate Session-Timers struct w/in the dialog */ if (!p->stimer) @@ -20226,17 +20275,15 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int /* Parse the Session-Expires header */ p_uac_se_hdr = get_header(req, "Session-Expires"); - if (!ast_strlen_zero(p_uac_se_hdr)) { - rtn = parse_session_expires(p_uac_se_hdr, &uac_max_se, &st_ref); - if (rtn != 0) { - transmit_response_reliable(p, "400 Session-Expires Invalid Syntax", req); - p->invitestate = INV_COMPLETED; - if (!p->lastinvite) { - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - } - res = -1; - goto request_invite_cleanup; + rtn = parse_session_expires(p_uac_se_hdr, &uac_max_se, &st_ref); + if (rtn != 0) { + transmit_response_reliable(p, "400 Session-Expires Invalid Syntax", req); + p->invitestate = INV_COMPLETED; + if (!p->lastinvite) { + sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); } + res = -1; + goto request_invite_cleanup; } /* Parse the Min-SE header */ @@ -21303,7 +21350,6 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, int firststate = AST_EXTENSION_REMOVED; struct sip_peer *authpeer = NULL; const char *eventheader = get_header(req, "Event"); /* Get Event package name */ - const char *acceptheader = get_header(req, "Accept"); int resubscribe = (p->subscribed != NONE); char *temp, *event; struct ao2_iterator i; @@ -21429,51 +21475,94 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, if (!strcmp(event, "presence") || !strcmp(event, "dialog")) { /* Presence, RFC 3842 */ unsigned int pidf_xml; + const char *accept; + int start = 0; + enum subscriptiontype subscribed = NONE; + const char *unknown_acceptheader = NULL; if (authpeer) /* We do not need the authpeer any more */ unref_peer(authpeer, "unref_peer, from handle_request_subscribe (authpeer 2)"); /* Header from Xten Eye-beam Accept: multipart/related, application/rlmi+xml, application/pidf+xml, application/xpidf+xml */ - pidf_xml = strstr(acceptheader, "application/pidf+xml") ? 1 : 0; - - /* Older versions of Polycom firmware will claim pidf+xml, but really - * they only support xpidf+xml. */ - if (pidf_xml && strstr(p->useragent, "Polycom")) { - p->subscribed = XPIDF_XML; - } else if (pidf_xml) { - p->subscribed = PIDF_XML; /* RFC 3863 format */ - } else if (strstr(acceptheader, "application/dialog-info+xml")) { - p->subscribed = DIALOG_INFO_XML; - /* IETF draft: draft-ietf-sipping-dialog-package-05.txt */ - } else if (strstr(acceptheader, "application/cpim-pidf+xml")) { - p->subscribed = CPIM_PIDF_XML; /* RFC 3863 format */ - } else if (strstr(acceptheader, "application/xpidf+xml")) { - p->subscribed = XPIDF_XML; /* Early pre-RFC 3863 format with MSN additions (Microsoft Messenger) */ - } else if (ast_strlen_zero(acceptheader)) { + accept = __get_header(req, "Accept", &start); + while ((subscribed == NONE) && !ast_strlen_zero(accept)) { + pidf_xml = strstr(accept, "application/pidf+xml") ? 1 : 0; + + /* Older versions of Polycom firmware will claim pidf+xml, but really + * they only support xpidf+xml. */ + if (pidf_xml && strstr(p->useragent, "Polycom")) { + subscribed = XPIDF_XML; + } else if (pidf_xml) { + subscribed = PIDF_XML; /* RFC 3863 format */ + } else if (strstr(accept, "application/dialog-info+xml")) { + subscribed = DIALOG_INFO_XML; + /* IETF draft: draft-ietf-sipping-dialog-package-05.txt */ + } else if (strstr(accept, "application/cpim-pidf+xml")) { + subscribed = CPIM_PIDF_XML; /* RFC 3863 format */ + } else if (strstr(accept, "application/xpidf+xml")) { + subscribed = XPIDF_XML; /* Early pre-RFC 3863 format with MSN additions (Microsoft Messenger) */ + } else { + unknown_acceptheader = accept; + } + /* check to see if there is another Accept header present */ + accept = __get_header(req, "Accept", &start); + } + + if (!start) { if (p->subscribed == NONE) { /* if the subscribed field is not already set, and there is no accept header... */ transmit_response(p, "489 Bad Event", req); - - ast_log(LOG_WARNING, "SUBSCRIBE failure: no Accept header: pvt: stateid: %d, laststate: %d, dialogver: %d, subscribecont: '%s', subscribeuri: '%s'\n", - p->stateid, p->laststate, p->dialogver, p->subscribecontext, p->subscribeuri); + ast_log(LOG_WARNING,"SUBSCRIBE failure: no Accept header: pvt: " + "stateid: %d, laststate: %d, dialogver: %d, subscribecont: " + "'%s', subscribeuri: '%s'\n", + p->stateid, + p->laststate, + p->dialogver, + p->subscribecontext, + p->subscribeuri); pvt_set_needdestroy(p, "no Accept header"); return 0; } /* if p->subscribed is non-zero, then accept is not obligatory; according to rfc 3265 section 3.1.3, at least. so, we'll just let it ride, keeping the value from a previous subscription, and not abort the subscription */ - } else { + } else if (subscribed == NONE) { /* Can't find a format for events that we know about */ char mybuf[200]; - snprintf(mybuf, sizeof(mybuf), "489 Bad Event (format %s)", acceptheader); + if (!ast_strlen_zero(unknown_acceptheader)) { + snprintf(mybuf, sizeof(mybuf), "489 Bad Event (format %s)", unknown_acceptheader); + } else { + snprintf(mybuf, sizeof(mybuf), "489 Bad Event"); + } transmit_response(p, mybuf, req); - - ast_log(LOG_WARNING, "SUBSCRIBE failure: unrecognized format: '%s' pvt: subscribed: %d, stateid: %d, laststate: %d, dialogver: %d, subscribecont: '%s', subscribeuri: '%s'\n", - acceptheader, (int)p->subscribed, p->stateid, p->laststate, p->dialogver, p->subscribecontext, p->subscribeuri); + ast_log(LOG_WARNING,"SUBSCRIBE failure: unrecognized format:" + "'%s' pvt: subscribed: %d, stateid: %d, laststate: %d," + "dialogver: %d, subscribecont: '%s', subscribeuri: '%s'\n", + unknown_acceptheader, + (int)p->subscribed, + p->stateid, + p->laststate, + p->dialogver, + p->subscribecontext, + p->subscribeuri); pvt_set_needdestroy(p, "unrecognized format"); return 0; + } else { + p->subscribed = subscribed; + } + } else if (!strcmp(event, "message-summary")) { + int start = 0; + int found_supported = 0; + const char *acceptheader; + + acceptheader = __get_header(req, "Accept", &start); + while (!found_supported && !ast_strlen_zero(acceptheader)) { + found_supported = strcmp(acceptheader, "application/simple-message-summary") ? 0 : 1; + if (!found_supported && (option_debug > 2)) { + ast_log(LOG_DEBUG, "Received SIP mailbox subscription for unknown format: %s\n", acceptheader); + } + acceptheader = __get_header(req, "Accept", &start); } - } else if (!strcmp(event, "message-summary")) { - if (!ast_strlen_zero(acceptheader) && strcmp(acceptheader, "application/simple-message-summary")) { + if (start && !found_supported) { /* Format requested that we do not support */ transmit_response(p, "406 Not Acceptable", req); ast_debug(2, "Received SIP mailbox subscription for unknown format: %s\n", acceptheader); @@ -21482,6 +21571,7 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, unref_peer(authpeer, "unref_peer, from handle_request_subscribe (authpeer 3)"); return 0; } + /* Looks like they actually want a mailbox status This version of Asterisk supports mailbox subscriptions The subscribed URI needs to exist in the dial plan @@ -22954,7 +23044,7 @@ enum st_mode st_get_mode(struct sip_pvt *p) static int sip_poke_noanswer(const void *data) { struct sip_peer *peer = (struct sip_peer *)data; - + peer->pokeexpire = -1; if (peer->lastms > -1) { @@ -22973,12 +23063,15 @@ static int sip_poke_noanswer(const void *data) peer->call = dialog_unref(peer->call, "unref dialog peer->call"); /* peer->call = sip_destroy(peer->call);*/ } - - peer->lastms = -1; - ast_devstate_changed(AST_DEVICE_UNKNOWN, "SIP/%s", peer->name); + + /* Don't send a devstate change if nothing changed. */ + if (peer->lastms > -1) { + peer->lastms = -1; + ast_devstate_changed(AST_DEVICE_UNKNOWN, "SIP/%s", peer->name); + } /* Try again quickly */ - AST_SCHED_REPLACE_UNREF(peer->pokeexpire, sched, + AST_SCHED_REPLACE_UNREF(peer->pokeexpire, sched, DEFAULT_FREQ_NOTOK, sip_poke_peer_s, peer, unref_peer(_data, "removing poke peer ref"), unref_peer(peer, "removing poke peer ref"), @@ -23772,6 +23865,8 @@ static void set_peer_defaults(struct sip_peer *peer) peer->timer_t1 = global_t1; peer->timer_b = global_timer_b; clear_peer_mailboxes(peer); + peer->transports = default_transports; + peer->default_outbound_transport = default_primary_transport; } /*! \brief Create temporary peer (used in autocreatepeer mode) */ @@ -23914,6 +24009,7 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str /* If we have realm authentication information, remove them (reload) */ clear_realm_authentication(peer->auth); peer->auth = NULL; + /* clear the transport information. We will detect if a default value is required after parsing the config */ peer->default_outbound_transport = 0; peer->transports = 0; @@ -25145,6 +25241,8 @@ static int reload_config(enum channelreloadreason reason) ast_netsock_set_qos(sipsock, global_tos_sip, global_cos_sip, "SIP"); } } + } else { + ast_netsock_set_qos(sipsock, global_tos_sip, global_cos_sip, "SIP"); } if (stunaddr.sin_addr.s_addr != 0) { ast_debug(1, "stun to %s:%d\n", @@ -25803,9 +25901,8 @@ static int sip_do_reload(enum channelreloadreason reason) start_poke = time(0); /* Prune peers who still are supposed to be deleted */ - ao2_t_callback(peers, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, peer_is_marked, NULL, - "callback to remove marked peers"); - + unlink_marked_peers_from_tables(); + ast_debug(4, "--------------- Done destroying pruned peers\n"); /* Send qualify (OPTIONS) to all peers */ |