[asterisk-commits] oej: branch oej/teapot-1.8 r386100 - /team/oej/teapot-1.8/patches/
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Fri Apr 19 07:00:05 CDT 2013
Author: oej
Date: Fri Apr 19 07:00:01 2013
New Revision: 386100
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=386100
Log:
Adding the patches used in this brew
Added:
team/oej/teapot-1.8/patches/
team/oej/teapot-1.8/patches/darjeeling-prack-1.8.diff (with props)
team/oej/teapot-1.8/patches/earl-grey-sip2cause-configurable-1.8.diff (with props)
team/oej/teapot-1.8/patches/roibos-cng-support-1.8.diff (with props)
Added: team/oej/teapot-1.8/patches/darjeeling-prack-1.8.diff
URL: http://svnview.digium.com/svn/asterisk/team/oej/teapot-1.8/patches/darjeeling-prack-1.8.diff?view=auto&rev=386100
==============================================================================
--- team/oej/teapot-1.8/patches/darjeeling-prack-1.8.diff (added)
+++ team/oej/teapot-1.8/patches/darjeeling-prack-1.8.diff Fri Apr 19 07:00:01 2013
@@ -1,0 +1,1182 @@
+Index: channels/chan_sip.c
+===================================================================
+--- channels/chan_sip.c (.../branches/1.8) (revision 369789)
++++ channels/chan_sip.c (.../team/oej/darjeeling-prack-1.8) (revision 369789)
+@@ -1242,10 +1242,10 @@
+ static int __transmit_response(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable);
+ static int retrans_pkt(const void *data);
+ static int transmit_response_using_temp(ast_string_field callid, struct ast_sockaddr *addr, int useglobal_nat, const int intended_method, const struct sip_request *req, const char *msg);
+-static int transmit_response(struct sip_pvt *p, const char *msg, const struct sip_request *req);
+ static int transmit_response_reliable(struct sip_pvt *p, const char *msg, const struct sip_request *req);
+ static int transmit_response_with_date(struct sip_pvt *p, const char *msg, const struct sip_request *req);
+ static int transmit_response_with_sdp(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable, int oldsdp, int rpid);
++static int transmit_response(struct sip_pvt *p, const char *msg, const struct sip_request *req);
+ static int transmit_response_with_unsupported(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *unsupported);
+ static int transmit_response_with_auth(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *rand, enum xmittype reliable, const char *header, int stale);
+ static int transmit_provisional_response(struct sip_pvt *p, const char *msg, const struct sip_request *req, int with_sdp);
+@@ -1266,6 +1266,7 @@
+ static int transmit_cc_notify(struct ast_cc_agent *agent, struct sip_pvt *subscription, enum sip_cc_notify_state state);
+ static int transmit_register(struct sip_registry *r, int sipmethod, const char *auth, const char *authheader);
+ static int send_response(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, uint32_t seqno);
++static void add_required_respheader(struct sip_request *req);
+ static int send_request(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, uint32_t seqno);
+ static void copy_request(struct sip_request *dst, const struct sip_request *src);
+ static void receive_message(struct sip_pvt *p, struct sip_request *req);
+@@ -3785,6 +3786,9 @@
+ if (sscanf(ast_str_buffer(pkt->data), "SIP/2.0 %30u", &respid) == 1) {
+ pkt->response_code = respid;
+ }
++ if (ast_test_flag(&p->flags[2], SIP_PAGE3_100REL) && respid > 100 && respid < 200) {
++ pkt->rseqno = p->rseq;
++ }
+ }
+ pkt->timer_t1 = p->timer_t1; /* Set SIP timer T1 */
+ pkt->retransid = -1;
+@@ -3966,7 +3970,7 @@
+
+ /*! \brief Acknowledges receipt of a packet and stops retransmission
+ * called with p locked*/
+-int __sip_ack(struct sip_pvt *p, uint32_t seqno, int resp, int sipmethod)
++int __sip_ack(struct sip_pvt *p, uint32_t seqno, int resp, int sipmethod, uint32_t rseqno)
+ {
+ struct sip_pkt *cur, *prev = NULL;
+ const char *msg = "Not Found"; /* used only for debugging */
+@@ -3985,6 +3989,10 @@
+ if (cur->seqno != seqno || cur->is_resp != resp) {
+ continue;
+ }
++ /* With PRACK we can have a situation with multiple unPRACKed responses */
++ if (rseqno && cur->rseqno != rseqno) {
++ continue;
++ }
+ if (cur->is_resp || cur->method == sipmethod) {
+ res = TRUE;
+ msg = "Found";
+@@ -3996,6 +4004,7 @@
+ if (sipdebug)
+ ast_debug(4, "** SIP TIMER: Cancelling retransmit of packet (reply received) Retransid #%d\n", cur->retransid);
+ }
++
+ /* This odd section is designed to thwart a
+ * race condition in the packet scheduler. There are
+ * two conditions under which deleting the packet from the
+@@ -4026,8 +4035,8 @@
+ break;
+ }
+ }
+- ast_debug(1, "Stopping retransmission on '%s' of %s %u: Match %s\n",
+- p->callid, resp ? "Response" : "Request", seqno, msg);
++ ast_debug(1, "Stopping retransmission on '%s' of %s %u: Match %s Rseq %u\n",
++ p->callid, resp ? "Response" : "Request", seqno, msg, rseqno);
+ return res;
+ }
+
+@@ -4045,7 +4054,7 @@
+ }
+ cur = p->packets;
+ method = (cur->method) ? cur->method : find_sip_method(cur->data->str);
+- __sip_ack(p, cur->seqno, cur->is_resp, method);
++ __sip_ack(p, cur->seqno, cur->is_resp, method, cur->rseqno);
+ }
+ }
+
+@@ -4158,11 +4167,63 @@
+ with_sdp ? send_provisional_keepalive_with_sdp : send_provisional_keepalive, dialog_ref(pvt, "Increment refcount to pass dialog pointer to sched callback"));
+ }
+
++/*! \brief Adds a Required header
++
++ Needs to be called before attachment (i.e. SDP) is added
++*/
++static void add_required_respheader(struct sip_request *req)
++{
++ if (req->reqsipoptions) {
++ char buf[SIPBUFSIZE] = "";
++ int i;
++
++ ast_debug(2, "=!=!=!=!= Plenty of required options for this message \n");
++
++ for (i = 0; i < ARRAY_LEN(sip_options); i++) {
++ if (req->reqsipoptions & sip_options[i].id) {
++ if (buf[0] != '\0') {
++ strncat(buf, ", ", sizeof(buf));
++ }
++ strncat(buf, sip_options[i].text, sizeof(buf));
++ ast_debug(3, "Found required response SIP option: %s\n", sip_options[i].text);
++ }
++ }
++ add_header(req, "Require", buf);
++ } else {
++ ast_debug(2, "=!=!=!=!= No required options for this message \n");
++ }
++
++}
++
++/*! \brief Active PRACK if supported by config and by other end */
++static void add_prack_respheader(struct sip_pvt *p, struct sip_request *req, int reliable)
++{
++ /* If method is INVITE and it contains Supported: 100 rel and we have enabled PRACK */
++ if (ast_test_flag(&p->flags[2], SIP_PAGE3_100REL)) {
++ /* Check if the invite has 100 REL supported here */
++ if (reliable == XMIT_PRACK) {
++ char buf[SIPBUFSIZE/2];
++ if (p->rseq == 0) {
++ p->rseq = 41; /* Starting level. Hi Douglas */
++ }
++ snprintf(buf, sizeof(buf), "%d", ++(p->rseq));
++ add_header(req, "Rseq", buf);
++ req->rseqno = p->rseq;
++ req->reqsipoptions |= SIP_OPT_100REL;
++ append_history(p, "PRACK", "PRACK Required: Our Rseq %u", p->rseq);
++ ast_debug(2, "=!=!=!=!=!=!=!= PRACK USED HERE. Rseq %u \n", p->rseq);
++ } else {
++ ast_debug(2, "=!=!=!=!=!=!=!= PRACK COULD BE USED HERE. Exactly HERE\n");
++ }
++ }
++}
++
+ /*! \brief Transmit response on SIP request*/
+ static int send_response(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, uint32_t seqno)
+ {
+ int res;
+
++
+ finalize_content(req);
+ add_blank(req);
+ if (sip_debug_test_pvt(p)) {
+@@ -4187,7 +4248,7 @@
+ }
+
+ res = (reliable) ?
+- __sip_reliable_xmit(p, seqno, 1, req->data, (reliable == XMIT_CRITICAL), req->method) :
++ __sip_reliable_xmit(p, seqno, 1, req->data, (reliable == XMIT_CRITICAL || reliable == XMIT_PRACK), req->method) :
+ __sip_xmit(p, req->data);
+ deinit_req(req);
+ if (res > 0) {
+@@ -6485,7 +6546,13 @@
+ struct sip_pvt *p = ast->tech_pvt;
+
+ sip_pvt_lock(p);
+- if (ast->_state != AST_STATE_UP) {
++ if (ast->_state != AST_STATE_UP && ast_test_flag(&p->flags[2], SIP_PAGE3_INVITE_WAIT_FOR_PRACK)) {
++ ast_set_flag(&p->flags[2], SIP_PAGE3_ANSWER_WAIT_FOR_PRACK);
++ ast_debug(2, "<-<-<--<-<-<-< HOLDING Answer while waiting for PRACK to arrive on channel %s\n", ast->name);
++ sip_pvt_unlock(p);
++ return 0;
++ }
++ if (ast->_state != AST_STATE_UP || ast_test_flag(&p->flags[2], SIP_PAGE3_INVITE_WAIT_FOR_PRACK)) {
+ try_suggested_sip_codec(p);
+
+ ast_setstate(ast, AST_STATE_UP);
+@@ -9873,13 +9940,15 @@
+ * is supported for this dialog. */
+ static int add_supported_header(struct sip_pvt *pvt, struct sip_request *req)
+ {
+- int res;
++ char supported[256] = "replaces";
++
+ if (st_get_mode(pvt, 0) != SESSION_TIMER_MODE_REFUSE) {
+- res = add_header(req, "Supported", "replaces, timer");
+- } else {
+- res = add_header(req, "Supported", "replaces");
++ strncat(supported, ", timer", sizeof(supported));
++ }
++ if (ast_test_flag(&pvt->flags[2], SIP_PAGE3_PRACK)) {
++ strncat(supported, ", 100rel", sizeof(supported));
+ }
+- return res;
++ return add_header(req, "Supported", supported);
+ }
+
+ /*! \brief Add header to SIP message */
+@@ -10315,6 +10384,7 @@
+ snprintf(se_hdr, sizeof(se_hdr), "%d;refresher=%s", p->stimer->st_interval,
+ strefresher2str(p->stimer->st_ref));
+ add_header(resp, "Session-Expires", se_hdr);
++ resp->reqsipoptions |= SIP_OPT_TIMER;
+ }
+
+ if (msg[0] == '2' && (p->method == SIP_SUBSCRIBE || p->method == SIP_REGISTER)) {
+@@ -10499,8 +10569,10 @@
+ {
+ struct sip_request resp;
+ uint32_t seqno = 0;
++ int res;
+
+- if (reliable && (sscanf(get_header(req, "CSeq"), "%30u ", &seqno) != 1)) {
++ res = sscanf(get_header(req, "CSeq"), "%30u ", &seqno);
++ if (reliable && res != 1) {
+ ast_log(LOG_WARNING, "Unable to determine sequence number from '%s'\n", get_header(req, "CSeq"));
+ return -1;
+ }
+@@ -10512,6 +10584,10 @@
+ ast_clear_flag(&p->flags[1], SIP_PAGE2_CONNECTLINEUPDATE_PEND);
+ add_rpid(&resp, p);
+ }
++ if (ast_test_flag(&p->flags[2], SIP_PAGE3_100REL) && strncmp(msg, "100", 3) && !strncmp(msg, "1", 1)) {
++ ast_debug(2, "=!=!=!=!=!= PRACK applied to message \"%s\" \n", msg);
++ reliable = XMIT_PRACK;
++ }
+ if (ast_test_flag(&p->flags[0], SIP_OFFER_CC)) {
+ add_cc_call_info_to_response(p, &resp);
+ }
+@@ -10551,6 +10627,10 @@
+ add_header(&resp, "X-Asterisk-HangupCauseCode", buf);
+ }
+ }
++ if (strncmp(msg, "100", 3)) {
++ add_prack_respheader(p, &resp, reliable);
++ add_required_respheader(&resp);
++ }
+ return send_response(p, &resp, reliable, seqno);
+ }
+
+@@ -10563,6 +10643,7 @@
+ }
+ respprep(&resp, p, msg, req);
+ add_header(&resp, "SIP-ETag", esc_entry->entity_tag);
++ add_required_respheader(&resp);
+
+ return send_response(p, &resp, 0, 0);
+ }
+@@ -10650,6 +10731,7 @@
+ respprep(&resp, p, msg, req);
+ append_date(&resp);
+ add_header(&resp, "Unsupported", unsupported);
++ add_required_respheader(&resp);
+ return send_response(p, &resp, XMIT_UNRELIABLE, 0);
+ }
+
+@@ -10703,6 +10785,7 @@
+ struct sip_request resp;
+ respprep(&resp, p, msg, req);
+ append_date(&resp);
++ add_required_respheader(&resp);
+ return send_response(p, &resp, XMIT_UNRELIABLE, 0);
+ }
+
+@@ -10712,6 +10795,7 @@
+ struct sip_request resp;
+ respprep(&resp, p, msg, req);
+ add_header(&resp, "Accept", "application/sdp");
++ add_required_respheader(&resp);
+ return send_response(p, &resp, reliable, 0);
+ }
+
+@@ -10724,6 +10808,7 @@
+ snprintf(tmp, sizeof(tmp), "%d", min_expiry);
+ respprep(&resp, p, msg, req);
+ add_header(&resp, "Min-Expires", tmp);
++ add_required_respheader(&resp);
+ return send_response(p, &resp, XMIT_UNRELIABLE, 0);
+ }
+
+@@ -11805,6 +11890,18 @@
+ if (rpid == TRUE) {
+ add_rpid(&resp, p);
+ }
++ if (ast_test_flag(&p->flags[2], SIP_PAGE3_100REL) && strncmp(msg, "100", 3) && !strncmp(msg, "1", 1)) {
++ ast_debug(2, "=!=!=!=!=!= PRACK applied to message \"%s\" \n", msg);
++ reliable = XMIT_PRACK;
++ }
++ if (strncmp(msg, "100", 3)) {
++ /* If we send a response WITH sdp we are not allowed to respond before the PRACK is received */
++ if (ast_test_flag(&p->flags[2], SIP_PAGE3_100REL)) {
++ ast_set_flag(&p->flags[2], SIP_PAGE3_INVITE_WAIT_FOR_PRACK);
++ }
++ add_prack_respheader(p, &resp, reliable);
++ add_required_respheader(&resp);
++ }
+ if (ast_test_flag(&p->flags[0], SIP_OFFER_CC)) {
+ add_cc_call_info_to_response(p, &resp);
+ }
+@@ -12214,7 +12311,69 @@
+ }
+
+ /*!
+- * \brief Build REFER/INVITE/OPTIONS/SUBSCRIBE message and transmit it
++ * \brief transmit SIP PRACK as a response to a provisional response with a Rseq and Require: 100rel header
++ */
++static int transmit_prack(struct sip_pvt *p, uint32_t their_rseq)
++{
++ int res;
++ int comparerseq = TRUE;
++ uint32_t focus_rseq = p->irseq;
++
++ /* During the early media phase, we could have a situation where we get provisional
++ responses from multiple devices, in separate early dialogs. In this case, this
++ code focuses on the FIRST early media response as the one in focus where we
++ check the rseq sequence numbers for retransmits and act upon them.
++ */
++
++ if (!ast_strlen_zero(p->theirtag_prack) && strcmp(p->theirtag, p->theirtag_prack)) {
++ /* We have already sent a PRACK in this dialog, but to a different device.
++ In this code, we focus on the first response that requires PRACK and do not check
++ the validity of rseq in responses in other early dialogs by controlling
++ the PRACK sequence numbers ordering.
++
++ To be 100% RFC correct, we should have a sip_pvt structure for each early dialog
++ and terminate them if we get a 199 response in that early dialog. these should
++ be organized in a tree-like structure based on the original
++ INVITE callid, cseq and from-tag.
++ */
++ comparerseq = FALSE;
++ }
++
++ if (comparerseq) {
++ if (their_rseq == p->irseq) {
++ ast_debug(3, "!?!?!?!?!? This is a retransmit of the previous response. %u \n", their_rseq);
++ /* RFC 3262: In particular, a UAC SHOULD NOT retransmit the PRACK request
++ when it receives a retransmission of the provisional response being
++ acknowledged, although doing so does not create a protocol error.*/
++ return -2; /* Not used by transmit_invite et al */
++ }
++ if (p->irseq > 0 && their_rseq != p->irseq + 1) {
++ ast_debug(3, "!?!?!?!?!? This is a response out of sequence! ignored. %u \n", their_rseq);
++ /* RFC 3262: if the UAC receives another reliable provisional
++ response to the same request, and its RSeq value is not one higher
++ than the value of the sequence number, that response MUST NOT be
++ acknowledged with a PRACK, and MUST NOT be processed further by the
++ UAC. An implementation MAY discard the response, or MAY cache the
++ response in the hopes of receiving the missing responses.
++ */
++ return -3;
++ }
++ }
++ p->irseq = their_rseq;
++ res = transmit_invite(p, SIP_PRACK, 0, 1, NULL);
++
++ if (ast_strlen_zero(p->theirtag_prack)) {
++ p->irseq = their_rseq;
++ ast_string_field_set(p, theirtag_prack, p->tag); /* Save this tag as a PRACK focus for this dialog */
++ } else {
++ p->irseq = focus_rseq;
++ }
++
++ return res;
++}
++
++/*!
++ * \brief Build PRACK/REFER/INVITE/OPTIONS/SUBSCRIBE message and transmit it
+ * \param p sip_pvt structure
+ * \param sipmethod
+ * \param sdp unknown
+@@ -12230,7 +12389,9 @@
+ req.method = sipmethod;
+ if (init) {/* Bump branch even on initial requests */
+ p->branch ^= ast_random();
+- p->invite_branch = p->branch;
++ if (sipmethod != SIP_PRACK) {
++ p->invite_branch = p->branch;
++ }
+ build_via(p);
+ }
+ if (init > 1) {
+@@ -12266,17 +12427,24 @@
+ }
+ snprintf(buf, sizeof(buf), "%d", p->expiry);
+ add_header(&req, "Expires", buf);
++ } else if (sipmethod == SIP_PRACK) {
++ /* Place holder */
++ /* Add headers for PRACK */
++ char buf[SIPBUFSIZE/2];
++ snprintf(buf, sizeof(buf), "%u %u %s", p->irseq, p->lastinvite, "INVITE");
++ add_header(&req, "RAck", buf);
+ }
+
+ /* This new INVITE is part of an attended transfer. Make sure that the
+ other end knows and replace the current call with this new call */
+ if (p->options && !ast_strlen_zero(p->options->replaces)) {
+ add_header(&req, "Replaces", p->options->replaces);
++ /* XXX This needs to be automated since we can have multiple options here */
+ add_header(&req, "Require", "replaces");
+ }
+
+ /* Add Session-Timers related headers */
+- if (st_get_mode(p, 0) == SESSION_TIMER_MODE_ORIGINATE) {
++ if (sipmethod == SIP_INVITE && st_get_mode(p, 0) == SESSION_TIMER_MODE_ORIGINATE) {
+ char i2astr[10];
+
+ if (!p->stimer->st_interval) {
+@@ -17604,6 +17772,7 @@
+ ast_cli(fd, " DirectMedia : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[0], SIP_DIRECT_MEDIA)));
+ ast_cli(fd, " PromiscRedir : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[0], SIP_PROMISCREDIR)));
+ ast_cli(fd, " User=Phone : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[0], SIP_USEREQPHONE)));
++ ast_cli(fd, " PRACK support: %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[2], SIP_PAGE3_PRACK)));
+ ast_cli(fd, " Video Support: %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[1], SIP_PAGE2_VIDEOSUPPORT) || ast_test_flag(&peer->flags[1], SIP_PAGE2_VIDEOSUPPORT_ALWAYS)));
+ ast_cli(fd, " Text Support : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[1], SIP_PAGE2_TEXTSUPPORT)));
+ ast_cli(fd, " Ign SDP ver : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[1], SIP_PAGE2_IGNORESDPVERSION)));
+@@ -17727,6 +17896,7 @@
+
+ /* - is enumerated */
+ astman_append(s, "SIP-DTMFmode: %s\r\n", dtmfmode2str(ast_test_flag(&peer->flags[0], SIP_DTMF)));
++ astman_append(s, "SIP-PRACK: %s\r\n", ast_test_flag(&peer->flags[2], SIP_PAGE3_PRACK) ? "Y" : "N");
+ astman_append(s, "ToHost: %s\r\n", peer->tohost);
+ astman_append(s, "Address-IP: %s\r\nAddress-Port: %d\r\n", ast_sockaddr_stringify_addr(&peer->addr), ast_sockaddr_port(&peer->addr));
+ astman_append(s, "Default-addr-IP: %s\r\nDefault-addr-port: %d\r\n", ast_sockaddr_stringify_addr(&peer->defaddr), ast_sockaddr_port(&peer->defaddr));
+@@ -18309,7 +18479,9 @@
+ ast_cli(a->fd, " Timer T1 minimum: %d\n", global_t1min);
+ ast_cli(a->fd, " Timer B: %d\n", global_timer_b);
+ ast_cli(a->fd, " No premature media: %s\n", AST_CLI_YESNO(global_prematuremediafilter));
++ ast_cli(a->fd, " Early media focus: %s\n", AST_CLI_YESNO(sip_cfg.early_media_focus));
+ ast_cli(a->fd, " Max forwards: %d\n", sip_cfg.default_max_forwards);
++ ast_cli(a->fd, " PRACK support: %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[2], SIP_PAGE3_PRACK)));
+
+ ast_cli(a->fd, "\nDefault Settings:\n");
+ ast_cli(a->fd, "-----------------\n");
+@@ -18681,10 +18853,14 @@
+ ast_cli(a->fd, " Format: %s\n", ast_getformatname_multiple(formatbuf, sizeof(formatbuf), cur->owner ? cur->owner->nativeformats : 0) );
+ ast_cli(a->fd, " T.38 support %s\n", AST_CLI_YESNO(cur->udptl != NULL));
+ ast_cli(a->fd, " Video support %s\n", AST_CLI_YESNO(cur->vrtp != NULL));
++ ast_cli(a->fd, " PRACK active %s\n", AST_CLI_YESNO(ast_test_flag(&cur->flags[2], SIP_PAGE3_100REL)));
+ ast_cli(a->fd, " MaxCallBR: %d kbps\n", cur->maxcallbitrate);
+ ast_cli(a->fd, " Theoretical Address: %s\n", ast_sockaddr_stringify(&cur->sa));
+ ast_cli(a->fd, " Received Address: %s\n", ast_sockaddr_stringify(&cur->recv));
+ ast_cli(a->fd, " SIP Transfer mode: %s\n", transfermode2str(cur->allowtransfer));
++ ast_cli(a->fd, " SIP PRACK support: %s\n", ast_test_flag(&cur->flags[2], SIP_PAGE3_100REL) ? "Active" :
++ (ast_test_flag(&cur->flags[2], SIP_PAGE3_PRACK) ? "Enabled" : "Disabled"));
++
+ ast_cli(a->fd, " Force rport: %s\n", AST_CLI_YESNO(ast_test_flag(&cur->flags[0], SIP_NAT_FORCE_RPORT)));
+ if (ast_sockaddr_isnull(&cur->redirip)) {
+ ast_cli(a->fd,
+@@ -19933,6 +20109,48 @@
+ return 0;
+ }
+
++/*! \brief Handle PRACK responses
++ */
++static void handle_response_prack(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, uint32_t seqno)
++{
++ ast_debug(2, "---> Got response on PRACK :: %d \n", resp);
++ /* Handle authentication early */
++ if (resp == 401 || resp == 407) {
++ if (p->options) {
++ p->options->auth_type = (resp == 401 ? WWW_AUTH : PROXY_AUTH);
++ }
++ if ((p->authtries == MAX_AUTHTRIES) || do_proxy_auth(p, req, resp, SIP_PRACK, 1)) {
++ ast_log(LOG_NOTICE, "Failed to authenticate on PRACK to '%s'\n", get_header(&p->initreq, "From"));
++ }
++ return;
++ }
++
++ /* THe REALLY important thing is that the PRACK request gets a response. The response itself
++ is not that important. A 481 means that the call will hang up. No response at all means
++ that the call will hang up
++ */
++ switch(resp) {
++ case 200: /* 200 OK - all is fine in the kingdom of SIP */
++ break;
++
++ case 408: /* Timeout */
++ case 481: /* Ok, they did not find our call ID. Let's die */
++ if (p->owner) {
++ ast_queue_hangup_with_cause(p->owner, hangup_sip2cause(resp));
++ }
++ break;
++ case 403: /* Forbidden */
++ case 415: /* Unsupported media type */
++ case 488: /* Not acceptable here */
++ case 606: /* Not Acceptable */
++ default:
++ /* Don't do anything */
++ break;
++ };
++
++
++}
++
+ /*!
+ * \brief Handle authentication challenge for SIP UPDATE
+ *
+@@ -20188,7 +20406,7 @@
+ ast_setstate(p->owner, AST_STATE_RINGING);
+ }
+ }
+- if (find_sdp(req)) {
++ if (!req->ignoresdp && find_sdp(req)) {
+ if (p->invitestate != INV_CANCELLED)
+ p->invitestate = INV_EARLY_MEDIA;
+ res = process_sdp(p, req, SDP_T38_NONE);
+@@ -20197,6 +20415,9 @@
+ ast_queue_control(p->owner, AST_CONTROL_PROGRESS);
+ }
+ ast_rtp_instance_activate(p->rtp);
++ if (sip_cfg.early_media_focus && ast_strlen_zero(p->theirtag_early)) {
++ ast_string_field_set(p, theirtag_early, p->tag);
++ }
+ }
+ check_pendings(p);
+ break;
+@@ -20260,7 +20481,7 @@
+ }
+ sip_handle_cc(p, req, AST_CC_CCNR);
+ }
+- if (find_sdp(req)) {
++ if (!req->ignoresdp && find_sdp(req)) {
+ if (p->invitestate != INV_CANCELLED)
+ p->invitestate = INV_EARLY_MEDIA;
+ res = process_sdp(p, req, SDP_T38_NONE);
+@@ -20268,6 +20489,9 @@
+ /* Queue a progress frame */
+ ast_queue_control(p->owner, AST_CONTROL_PROGRESS);
+ }
++ if (sip_cfg.early_media_focus && ast_strlen_zero(p->theirtag_early)) {
++ ast_string_field_set(p, theirtag_early, p->tag);
++ }
+ ast_rtp_instance_activate(p->rtp);
+ } else {
+ /* Alcatel PBXs are known to send 183s with no SDP after sending
+@@ -20371,6 +20595,10 @@
+
+ /* Check for Session-Timers related headers */
+ if (st_get_mode(p, 0) != SESSION_TIMER_MODE_REFUSE && p->outgoing_call == TRUE && !reinvite) {
++ /* XXX Code should check in response if there's a "Require: timer"
++ header. If there is, sessions timer is enabled for this dialog
++ If not, only this side (UAC) do session timers.
++ */
+ p_hdrval = (char*)get_header(req, "Session-Expires");
+ if (!ast_strlen_zero(p_hdrval)) {
+ /* UAS supports Session-Timers */
+@@ -21073,6 +21301,9 @@
+ struct ast_channel *owner;
+ int sipmethod;
+ const char *c = get_header(req, "Cseq");
++ const char *required = get_header(req, "Require");
++ char tag[128];
++
+ /* GCC 4.2 complains if I try to cast c as a char * when passing it to ast_skip_nonblanks, so make a copy of it */
+ char *c_copy = ast_strdupa(c);
+ /* Skip the Cseq and its subsequent spaces */
+@@ -21114,7 +21345,7 @@
+ ack_res = __sip_semi_ack(p, seqno, 0, sipmethod);
+ }
+ } else {
+- ack_res = __sip_ack(p, seqno, 0, sipmethod);
++ ack_res = __sip_ack(p, seqno, 0, sipmethod, 0);
+ }
+
+ if (ack_res == FALSE) {
+@@ -21133,13 +21364,14 @@
+ p->pendinginvite = 0;
+ }
+
+- /* Get their tag if we haven't already */
+- if (ast_strlen_zero(p->theirtag) || (resp >= 200)) {
+- char tag[128];
++
++ /* Always get the tag. Find_call will filter out after we have an established dialog,
++ so that we don't update the tag after a 200 or other final response.
++ Provided that SIP pedantic checking is turned on of course.
++ */
++ gettag(req, "To", tag, sizeof(tag));
++ ast_string_field_set(p, theirtag, tag);
+
+- gettag(req, "To", tag, sizeof(tag));
+- ast_string_field_set(p, theirtag, tag);
+- }
+ /* This needs to be configurable on a channel/peer level,
+ not mandatory for all communication. Sadly enough, NAT implementations
+ are not so stable so we can always rely on these headers.
+@@ -21159,7 +21391,50 @@
+ pvt_set_needdestroy(p, "received 4XX response to a BYE");
+ return;
+ }
++
++ /* If we have a required header in the response, the other side have activated an extension
++ we said that we do support */
++ if (!ast_strlen_zero(required)) {
++ int activeextensions = parse_required_sip_options(required);
++ if (activeextensions & SIP_OPT_100REL) {
+
++ const char *rseq = get_header(req, "RSeq");
++ uint32_t their_rseq;
++ int res;
++ ast_debug(3, "!=!=!=!=!=! Response relies on PRACK! Rseq %s\n", rseq);
++
++ /* XXX If the response relies on PRACK, we need to start a PRACK transaction
++ */
++ sscanf(get_header(req, "RSeq"), "%30u ", &their_rseq);
++ append_history(p, "TxPrack", "Their Rseq %u\n", their_rseq);
++ parse_ok_contact(p, req);
++ build_route(p, req, 1, resp);
++
++ res = transmit_prack(p, their_rseq);
++ if (res == -2) {
++ /* This response is a retransmit and should be ignored */
++ /* RFC 3262: Once a reliable provisional response is received, retransmissions of
++ that response MUST be discarded. A response is a retransmission when
++ its dialog ID, CSeq, and RSeq match the original response.
++ */
++ append_history(p, "PrIgnore", "Ignoring this retransmit (PRACK active)\n");
++ return;
++ } else if (res == -3) {
++ append_history(p, "PrIgnore", "Ignoring this response - out of order (PRACK active)\n");
++ return;
++ }
++ }
++ if (activeextensions & SIP_OPT_TIMER) {
++ ast_debug(3, "!=!=!=!=!=! The other side activated Session timers! \n");
++ }
++ }
++
++ if (sip_cfg.early_media_focus && !ast_strlen_zero(p->theirtag_early) && strcmp(p->theirtag_early, p->theirtag)) {
++ /* If we already are in early media phase, and have a response from a new device in this call we should
++ ignore the SDP. */
++ req->ignoresdp = TRUE;
++ }
++
+ if (p->relatedpeer && sipmethod == SIP_OPTIONS) {
+ /* We don't really care what the response is, just that it replied back.
+ Well, as long as it's not a 100 response... since we might
+@@ -21176,6 +21451,9 @@
+ } else if (sipmethod == SIP_INFO) {
+ /* More good gravy! */
+ handle_response_info(p, resp, rest, req, seqno);
++ } else if (sipmethod == SIP_PRACK) {
++ /* More good candy! */
++ handle_response_prack(p, resp, rest, req, seqno);
+ } else if (sipmethod == SIP_MESSAGE) {
+ /* More good gravy! */
+ handle_response_message(p, resp, rest, req, seqno);
+@@ -22323,6 +22601,37 @@
+ return 0;
+ }
+
++/*! Support for the SIP Prack method
++ */
++static int handle_request_prack(struct sip_pvt *p, struct sip_request *req)
++{
++ const char *rack = get_header(req, "RAck");
++ uint32_t rseq, cseq;
++
++ if(sscanf(rack, "%30u %30u", &rseq, &cseq) != 2) {
++ /* we did not get proper rseq/cseq */
++ transmit_response(p, "481 Could not get proper rseq/cseq in Rack", req);
++ }
++ ast_debug(3, "!=!=!=!=!=!= Got PRACK with rseq %u and cseq %u \n", rseq, cseq);
++ if (rseq <= p->rseq) {
++ /* Ack the retransmits */
++ int acked = __sip_ack(p, cseq, 1 /* response */, 0, rseq);
++ ast_debug(2, "!=!=!=!=!=! Tried acking the response - %s \n", acked ? "Sucess" : "Total utterly failure");
++ }
++ append_history(p, "PRACK", "PRACK received Rseq %u", rseq);
++ transmit_response(p, "200 OK", req);
++ if (ast_test_flag(&p->flags[2], SIP_PAGE3_ANSWER_WAIT_FOR_PRACK)) {
++ /* If the response sent reliably contained an SDP, we're not allowed to answer
++ until we have a PRACK response
++ */
++ ast_debug(2, "-<-<--<-<-<-<- Finally a good time to answer call (PRACK arrived) %s \n", p->owner->name);
++ ast_clear_flag(&p->flags[2], SIP_PAGE3_ANSWER_WAIT_FOR_PRACK);
++ sip_answer(p->owner);
++ }
++ ast_clear_flag(&p->flags[2], SIP_PAGE3_INVITE_WAIT_FOR_PRACK); /* Clear flag */
++ return 0;
++}
++
+ /*!
+ * \brief Handle incoming INVITE request
+ * \note If the INVITE has a Replaces header, it is part of an
+@@ -22384,7 +22693,7 @@
+ p->invitestate = INV_COMPLETED;
+ if (!p->lastinvite)
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+- res = -1;
++ res = 0;
+ goto request_invite_cleanup;
+ }
+ }
+@@ -22394,6 +22703,23 @@
+ p->sipoptions |= required_profile;
+ p->reqsipoptions = required_profile;
+
++ /* Check if the request supports or require PRACK */
++ if (p->reqsipoptions & SIP_OPT_100REL || p->sipoptions & SIP_OPT_100REL) {
++ if (ast_test_flag(&p->flags[2], SIP_PAGE3_PRACK)) { /* Is PRACK enabled for this dialog? */
++ ast_set_flag(&p->flags[2], SIP_PAGE3_100REL); /* Mark PRACK as active for this dialog */
++ ast_debug(2, "--#-#-#-#- Adding PRACK support for this dialog \n");
++ } else if (p->reqsipoptions & SIP_OPT_100REL) {
++ /* If PRACK was required but is disabled in configuration, don't play */
++ transmit_response(p, "420 Bad extension (unsupported)", req);
++ p->invitestate = INV_COMPLETED;
++ if (!p->lastinvite) {
++ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
++ }
++ res = 0;
++ goto request_invite_cleanup;
++ }
++ }
++
+ /* Check if this is a loop */
+ if (ast_test_flag(&p->flags[0], SIP_OUTGOING) && p->owner && (p->invitestate != INV_TERMINATED && p->invitestate != INV_CONFIRMED) && p->owner->_state != AST_STATE_UP) {
+ /* This is a call to ourself. Send ourselves an error code and stop
+@@ -22456,7 +22782,7 @@
+ * transaction. Calling __sip_ack will take care of this by clearing the p->pendinginvite and removing the response
+ * from the previous transaction from the list of outstanding packets.
+ */
+- __sip_ack(p, p->pendinginvite, 1, 0);
++ __sip_ack(p, p->pendinginvite, 1, 0, 0);
+ } else {
+ /* We already have a pending invite. Sorry. You are on hold. */
+ p->glareinvite = seqno;
+@@ -24552,7 +24878,7 @@
+ return 0;
+ } else if (auth_result == AUTH_SUCCESSFUL && p->lastinvite) {
+ /* We need to stop retransmitting the 401 */
+- __sip_ack(p, p->lastinvite, 1, 0);
++ __sip_ack(p, p->lastinvite, 1, 0, 0);
+ }
+
+ publish_type = determine_sip_publish_type(req, event, etag, expires_str, &expires_int);
+@@ -25379,12 +25705,15 @@
+ case SIP_UPDATE:
+ res = handle_request_update(p, req);
+ break;
++ case SIP_PRACK:
++ res = handle_request_prack(p, req);
++ break;
+ case SIP_ACK:
+ /* Make sure we don't ignore this */
+ if (seqno == p->pendinginvite) {
+ p->invitestate = INV_TERMINATED;
+ p->pendinginvite = 0;
+- acked = __sip_ack(p, seqno, 1 /* response */, 0);
++ acked = __sip_ack(p, seqno, 1 /* response */, 0, 0);
+ if (find_sdp(req)) {
+ if (process_sdp(p, req, SDP_T38_NONE))
+ return -1;
+@@ -25393,7 +25722,7 @@
+ } else if (p->glareinvite == seqno) {
+ /* handle ack for the 491 pending sent for glareinvite */
+ p->glareinvite = 0;
+- acked = __sip_ack(p, seqno, 1, 0);
++ acked = __sip_ack(p, seqno, 1, 0, 0);
+ }
+ if (!acked) {
+ /* Got an ACK that did not match anything. Ignore
+@@ -27037,6 +27366,9 @@
+ } else if (!strcasecmp(v->name, "buggymwi")) {
+ ast_set_flag(&mask[1], SIP_PAGE2_BUGGY_MWI);
+ ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_BUGGY_MWI);
++ } else if (!strcasecmp(v->name, "prack")) {
++ ast_set_flag(&mask[2], SIP_PAGE3_PRACK);
++ ast_set2_flag(&flags[2], ast_true(v->value), SIP_PAGE3_PRACK);
+ } else
+ res = 0;
+
+@@ -28244,6 +28576,7 @@
+ externtcpport = STANDARD_SIP_PORT;
+ externtlsport = STANDARD_TLS_PORT;
+ sip_cfg.srvlookup = DEFAULT_SRVLOOKUP;
++ sip_cfg.early_media_focus = DEFAULT_EARLY_MEDIA_FOCUS;
+ global_tos_sip = DEFAULT_TOS_SIP;
+ global_tos_audio = DEFAULT_TOS_AUDIO;
+ global_tos_video = DEFAULT_TOS_VIDEO;
+@@ -28597,6 +28930,8 @@
+ global_match_auth_username = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "srvlookup")) {
+ sip_cfg.srvlookup = ast_true(v->value);
++ } else if (!strcasecmp(v->name, "earlymediafocus")) {
++ sip_cfg.early_media_focus = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "pedantic")) {
+ sip_cfg.pedanticsipchecking = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "maxexpirey") || !strcasecmp(v->name, "maxexpiry")) {
+Index: channels/sip/include/sip.h
+===================================================================
+--- channels/sip/include/sip.h (.../branches/1.8) (revision 369789)
++++ channels/sip/include/sip.h (.../team/oej/darjeeling-prack-1.8) (revision 369789)
+@@ -155,7 +155,7 @@
+ * \todo This string should be set dynamically. We only support REFER and SUBSCRIBE if we have
+ * allowsubscribe and allowrefer on in sip.conf.
+ */
+-#define ALLOWED_METHODS "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH"
++#define ALLOWED_METHODS "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH, PRACK"
+
+ /*! \brief Standard SIP unsecure port for UDP and TCP from RFC 3261. DO NOT CHANGE THIS */
+ #define STANDARD_SIP_PORT 5060
+@@ -187,6 +187,7 @@
+ #define DEFAULT_MWI_FROM ""
+ #define DEFAULT_NOTIFYMIME "application/simple-message-summary"
+ #define DEFAULT_ALLOWGUEST TRUE
++#define DEFAULT_EARLY_MEDIA_FOCUS FALSE; /*!< Focus on a single early media stream */
+ #define DEFAULT_RTPKEEPALIVE 0 /*!< Default RTPkeepalive setting */
+ #define DEFAULT_CALLCOUNTER FALSE /*!< Do not enable call counters by default */
+ #define DEFAULT_SRVLOOKUP TRUE /*!< Recommended setting is ON */
+@@ -223,6 +224,7 @@
+ #define DEFAULT_CAPABILITY (AST_FORMAT_ULAW | AST_FORMAT_TESTLAW | AST_FORMAT_ALAW | AST_FORMAT_GSM | AST_FORMAT_H263);
+ #define DEFAULT_STORE_SIP_CAUSE FALSE /*!< Don't store HASH(SIP_CAUSE,<channel name>) for channels by default */
+ #endif
++#define DEFAULT_PRACK FALSE /*!< Default: Prack is turned off */
+ /*@}*/
+
+ /*! \name SIPflags
+@@ -356,10 +358,14 @@
+ SIP_PAGE2_Q850_REASON | SIP_PAGE2_HAVEPEERCONTEXT | SIP_PAGE2_USE_SRTP)
+
+
+-#define SIP_PAGE3_SNOM_AOC (1 << 0) /*!< DPG: Allow snom aoc messages */
++#define SIP_PAGE3_SNOM_AOC (1 << 0) /*!< DPG: Allow snom aoc messages */
++#define SIP_PAGE3_PRACK (1 << 1) /*!< DPG: Allow snom aoc messages */
++#define SIP_PAGE3_100REL (1 << 2) /*!< D: If PRACK is active for a specific dialog */
++#define SIP_PAGE3_INVITE_WAIT_FOR_PRACK (1 << 3) /*!< D: Wait for PRACK response before sending 200 OK */
++#define SIP_PAGE3_ANSWER_WAIT_FOR_PRACK (1 << 4) /*!< D: Send ANSWER when PRACK is received */
+
+ #define SIP_PAGE3_FLAGS_TO_COPY \
+- (SIP_PAGE3_SNOM_AOC)
++ (SIP_PAGE3_SNOM_AOC | SIP_PAGE3_PRACK)
+
+ /*@}*/
+
+@@ -403,6 +409,7 @@
+ * where the original response would be sent RELIABLE in an INVITE transaction
+ */
+ enum xmittype {
++ XMIT_PRACK = 3, /*!< Transmit response the PRACK way: reliably, with re-transmits. */
+ XMIT_CRITICAL = 2, /*!< Transmit critical SIP message reliably, with re-transmits.
+ * If it fails, it's critical and will cause a teardown of the session */
+ XMIT_RELIABLE = 1, /*!< Transmit SIP message reliably, with re-transmits */
+@@ -560,7 +567,7 @@
+ SIP_NOTIFY, /*!< Status update, Part of the event package standard, result of a SUBSCRIBE or a REFER */
+ SIP_INVITE, /*!< Set up a session */
+ SIP_ACK, /*!< End of a three-way handshake started with INVITE. */
+- SIP_PRACK, /*!< Reliable pre-call signalling. Not supported in Asterisk. */
++ SIP_PRACK, /*!< Reliable pre-call signalling. */
+ SIP_BYE, /*!< End of a session */
+ SIP_REFER, /*!< Refer to another URI (transfer) */
+ SIP_SUBSCRIBE, /*!< Subscribe for updates (voicemail, session status, device status, presence) */
+@@ -675,6 +682,7 @@
+ be applied to devices (trunks, services, phones)
+ */
+ struct sip_settings {
++ int early_media_focus; /*!< G: Focus on the first early media stream received, ignore the rest */
+ int peer_rtupdate; /*!< G: Update database with registration data for peer? */
+ int rtsave_sysname; /*!< G: Save system name at registration? */
+ int ignore_regexpire; /*!< G: Ignore expiration of peer */
+@@ -749,12 +757,15 @@
+ int headers; /*!< # of SIP Headers */
+ int method; /*!< Method of this request */
+ int lines; /*!< Body Content */
++ uint32_t rseqno; /*!< PRACK Rseq */
++ unsigned int reqsipoptions; /*!< Required SIP options for this answer */
+ unsigned int sdp_start; /*!< the line number where the SDP begins */
+ unsigned int sdp_count; /*!< the number of lines of SDP */
+ char debug; /*!< print extra debugging if non zero */
+ char has_to_tag; /*!< non-zero if packet has To: tag */
+ char ignore; /*!< if non-zero This is a re-transmit, ignore it */
+ char authenticated; /*!< non-zero if this request was authenticated */
++ char ignoresdp; /*!< In some cases, we have to ignore the SDP in responses */
+ ptrdiff_t header[SIP_MAX_HEADERS]; /*!< Array of offsets into the request string of each SIP header*/
+ ptrdiff_t line[SIP_MAX_LINES]; /*!< Array of offsets into the request string of each SDP line*/
+ struct ast_str *data;
+@@ -959,6 +970,8 @@
+ AST_STRING_FIELD(rdnis); /*!< Referring DNIS */
+ AST_STRING_FIELD(redircause); /*!< Referring cause */
+ AST_STRING_FIELD(theirtag); /*!< Their tag */
++ AST_STRING_FIELD(theirtag_prack); /*!< Current tag focus for PRACK handling */
++ AST_STRING_FIELD(theirtag_early); /*!< Current tag focus for early media handling */
+ AST_STRING_FIELD(tag); /*!< Our tag for this session */
+ AST_STRING_FIELD(username); /*!< [user] name */
+ AST_STRING_FIELD(peername); /*!< [peer] name, not set if [user] */
+@@ -985,6 +998,8 @@
+ uint32_t ocseq; /*!< Current outgoing seqno */
+ uint32_t icseq; /*!< Current incoming seqno */
+ uint32_t init_icseq; /*!< Initial incoming seqno from first request */
++ uint32_t rseq; /*!< Current PRACK rseq on our side*/
++ uint32_t irseq; /*!< Current PRACK rseq on their side*/
+ ast_group_t callgroup; /*!< Call group */
+ ast_group_t pickupgroup; /*!< Pickup group */
+ uint32_t lastinvite; /*!< Last seqno of invite */
+@@ -1142,6 +1157,7 @@
+ int retrans; /*!< Retransmission number */
+ int method; /*!< SIP method for this packet */
+ uint32_t seqno; /*!< Sequence number */
++ uint32_t rseqno; /*!< PRACK Sequence number */
+ char is_resp; /*!< 1 if this is a response packet (e.g. 200 OK), 0 if it is a request */
+ char is_fatal; /*!< non-zero if there is a fatal error */
+ int response_code; /*!< If this is a response, the response code */
+@@ -1751,7 +1767,7 @@
+ char * const text; /*!< Text id, as in standard */
+ } sip_options[] = { /* XXX used in 3 places */
+ /* RFC3262: PRACK 100% reliability */
+- { SIP_OPT_100REL, NOT_SUPPORTED, "100rel" },
++ { SIP_OPT_100REL, SUPPORTED, "100rel" },
+ /* RFC3959: SIP Early session support */
+ { SIP_OPT_EARLY_SESSION, NOT_SUPPORTED, "early-session" },
+ /* SIMPLE events: RFC4662 */
+Index: channels/sip/include/reqresp_parser.h
+===================================================================
[... 2029 lines stripped ...]
More information about the asterisk-commits
mailing list