CANCELs and negative ACKs are constructed from the INVITE which was sent out
authorMiklos Tirpak <miklos@iptel.org>
Tue, 29 May 2007 15:52:37 +0000 (15:52 +0000)
committerMiklos Tirpak <miklos@iptel.org>
Tue, 29 May 2007 15:52:37 +0000 (15:52 +0000)
instead of building them from the received one. The disadvantage is
that the outgoing INVITE has to be partially reparsed, the advantage is that
the CANCEL/ACK is always RFC-compliant, it always contains the same route-set
as the INVITE message. (closes SER-212)

build_local_reparse() function can be used to construct a CANCEL or ACK, it
reparses the outgoing INVITE and applies the modifications specified by the RFC.

new module parameters:
 - reparse_invite: set it to 0 in order to revert to the old behaviour)
 - ac_extra_hdrs: prefix for additional headers which are kept by build_local_reparse()

new script function:
 - t_relay_cancel() - can be used to catch CANCEL requests at the beginning of the script
                      and bypass the rest of the script processing:

if (!t_relay_cancel()) {  # implicit drop if relaying was successful,
                          # nothing to do

        # corresponding INVITE transaction found but error occurred
        sl_reply("500", "Internal Server Error");
        drop;
}
# bad luck, corresponding INVITE transaction is missing,
# do the same as for INVITEs
...

modules/tm/lw_parser.c [new file with mode: 0644]
modules/tm/lw_parser.h [new file with mode: 0644]
modules/tm/t_cancel.c
modules/tm/t_fwd.c
modules/tm/t_fwd.h
modules/tm/t_msgbuilder.c
modules/tm/t_msgbuilder.h
modules/tm/t_reply.c
modules/tm/tm.c

diff --git a/modules/tm/lw_parser.c b/modules/tm/lw_parser.c
new file mode 100644 (file)
index 0000000..c37f732
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2007 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    info@iptel.org
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * 2007-05-28  lightweight parser implemented for build_local_reparse()
+ *              function. Basically copy-pasted from the core parser (Miklos)
+ */
+
+#include "../../parser/keys.h"
+#include "../../parser/hf.h"
+#include "../../parser/parser_f.h"
+#include "lw_parser.h"
+
+/* macros from the core parser */
+#define LOWER_BYTE(b) ((b) | 0x20)
+#define LOWER_DWORD(d) ((d) | 0x20202020)
+
+#define READ(val) \
+(*(val + 0) + (*(val + 1) << 8) + (*(val + 2) << 16) + (*(val + 3) << 24))
+
+/*
+ * lightweight header field name parser
+ * used by build_local_reparse() function in order to construct ACK or CANCEL request
+ * from the INVITE buffer
+ * this parser supports only the header fields which are needed by build_local_reparse()
+ */
+char *lw_get_hf_name(char *begin, char *end,
+                       enum _hdr_types_t *type)
+{
+
+       char            *p;
+       unsigned int    val;
+
+       if (end - begin < 4) {
+               *type = HDR_ERROR_T;
+               return begin;
+       }
+
+       p = begin;
+       val = LOWER_DWORD(READ(p));
+
+       switch(val) {
+
+       case _cseq_:    /* Cseq */
+               *type = HDR_CSEQ_T;
+               p += 4;
+               break;
+
+       case _via1_:    /* Via */
+       case _via2_:
+               *type = HDR_VIA_T;
+               p += 3;
+               break;
+
+       case _from_:    /* From */
+               *type = HDR_FROM_T;
+               p += 4;
+               break;
+
+       case _to12_:    /* To */
+               *type = HDR_TO_T;
+               p += 2;
+               break;
+
+       case _requ_:    /* Require */
+               p += 4;
+               val = LOWER_DWORD(READ(p));
+
+               switch(val) {
+
+               case _ire1_:
+               case _ire2_:
+                       p += 3;
+                       *type = HDR_REQUIRE_T;
+                       break;
+
+               default:
+                       p -= 4;
+                       *type = HDR_OTHER_T;
+                       break;
+               }
+               break;
+
+       case _prox_:    /* Proxy-Require */
+
+               if ((LOWER_DWORD(READ(p+4)) == _y_re_)
+               && (LOWER_DWORD(READ(p+8)) == _quir_)
+               && (LOWER_BYTE(*(p+12)) == 'e')) {
+
+                       p += 13;
+                       *type = HDR_PROXYREQUIRE_T;
+                       break;
+
+               } else {
+                       *type = HDR_OTHER_T;
+                       break;
+               }
+
+       case _cont_:    /* Content-Length */
+
+               if ((LOWER_DWORD(READ(p+4)) == _ent__)
+               && (LOWER_DWORD(READ(p+8)) == _leng_)
+               && (LOWER_BYTE(*(p+12)) == 't')
+               && (LOWER_BYTE(*(p+13)) == 'h')) {
+
+                       p += 14;
+                       *type = HDR_CONTENTLENGTH_T;
+                       break;
+               } else {
+                       *type = HDR_OTHER_T;
+                       break;
+               }
+
+       case _call_:    /* Call-Id */
+
+               p += 4;
+               val = LOWER_DWORD(READ(p));
+
+               switch(val) {
+
+               case __id1_:
+               case __id2_:
+                       p += 7;
+                       *type = HDR_CALLID_T;
+                       break;
+
+               default:
+                       p -= 4;
+                       *type = HDR_OTHER_T;
+                       break;
+               }
+               break;
+
+       case _rout_:    /* Route */
+
+               if (LOWER_BYTE(*(p+4)) == 'e') {
+                       p += 5;
+                       *type = HDR_ROUTE_T;
+                       break;
+               } else {
+                       *type = HDR_OTHER_T;
+                       break;
+               }
+
+       case _max__:    /* Max-Forwards */
+
+               if ((LOWER_DWORD(READ(p+4)) == _forw_)
+               && (LOWER_DWORD(READ(p+8)) == _ards_)) {
+
+                       p += 12;
+                       *type = HDR_MAXFORWARDS_T;
+                       break;
+               } else {
+                       *type = HDR_OTHER_T;
+                       break;
+               }
+
+       default:
+               /* compact headers */
+               switch(LOWER_BYTE(*p)) {
+
+               case 'v':       /* Via */
+                       if ((*(p+1) == ' ') || (*(p+1) == ':')) {
+                               p++;
+                               *type = HDR_VIA_T;
+                               break;
+                       }
+                       *type = HDR_OTHER_T;
+                       break;
+
+               case 'f':       /* From */
+                       if ((*(p+1) == ' ') || (*(p+1) == ':')) {
+                               p++;
+                               *type = HDR_FROM_T;
+                               break;
+                       }
+                       *type = HDR_OTHER_T;
+                       break;
+
+               case 't':       /* To */
+                       if (LOWER_BYTE(*(p+1)) == 'o') {
+                               p += 2;
+                               *type = HDR_TO_T;
+                               break;
+                       }
+                       if ((*(p+1) == ' ') || (*(p+1) == ':')) {
+                               p++;
+                               *type = HDR_TO_T;
+                               break;
+                       }
+                       *type = HDR_OTHER_T;
+                       break;
+
+               case 'l':       /* Content-Length */
+                       if ((*(p+1) == ' ') || (*(p+1) == ':')) {
+                               p++;
+                               *type = HDR_CONTENTLENGTH_T;
+                               break;
+                       }
+                       *type = HDR_OTHER_T;
+                       break;
+
+               case 'i':       /* Call-Id */
+                       if ((*(p+1) == ' ') || (*(p+1) == ':')) {
+                               p++;
+                               *type = HDR_CALLID_T;
+                               break;
+                       }
+                       *type = HDR_OTHER_T;
+                       break;
+
+               default:
+                       *type = HDR_OTHER_T;
+                       break;
+               }
+       }
+
+       return p;
+}
+
+/* returns a pointer to the next line */
+char *lw_next_line(char *buf, char *buf_end)
+{
+       char    *c;
+
+       c = buf;
+       do {
+               while ((c < buf_end) && (*c != '\n')) c++;
+               if (c < buf_end) c++;
+               if ((c < buf_end) && (*c == '\r')) c++;
+
+       } while ((c < buf_end) &&
+               ((*c == ' ') || (*c == '\t'))); /* next line begins with whitespace line folding */
+
+       return c;
+}
diff --git a/modules/tm/lw_parser.h b/modules/tm/lw_parser.h
new file mode 100644 (file)
index 0000000..902084f
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2007 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    info@iptel.org
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * 2007-05-28  lightweight parser implemented for build_local_reparse()
+ *              function. Basically copy-pasted from the core parser (Miklos)
+ */
+
+#ifndef _LW_PARSER_H
+#define _LW_PARSER_H
+
+/*
+ * lightweight header field name parser
+ * used by build_local_reparse() function in order to construct ACK or CANCEL request
+ * from the INVITE buffer
+ * this parser supports only the header fields which are needed by build_local_reparse()
+ */
+char *lw_get_hf_name(char *begin, char *end,
+                       enum _hdr_types_t *type);
+
+/* returns a pointer to the next line */
+char *lw_next_line(char *buf, char *buf_end);
+
+#endif /* _LW_PARSER_H */
index 1a619c1..1c98d6f 100644 (file)
@@ -33,6 +33,9 @@
  * 2004-02-13  timer_link.payload removed (bogdan)
  * 2006-10-10  cancel_uacs  & cancel_branch take more options now (andrei)
  * 2007-03-15  TMCB_ONSEND hooks added (andrei)
+ * 2007-05-28: cancel_branch() constructs the CANCEL from the
+ *             outgoing INVITE instead of the incomming one.
+ *             (it can be disabled with reparse_invite=0) (Miklos)
  */
 
 #include <stdio.h> /* for FILE* in fifo_uac_cancel */
@@ -175,8 +178,14 @@ int cancel_branch( struct cell *t, int branch, int flags )
                        return 1;
                }
        }
-       
-       cancel = build_local(t, branch, &len, CANCEL, CANCEL_LEN, &t->to);
+
+       if (reparse_invite) {
+               /* build the CANCEL from the INVITE which was sent out */
+               cancel = build_local_reparse(t, branch, &len, CANCEL, CANCEL_LEN, &t->to);
+       } else {
+               /* build the CANCEL from the reveived INVITE */
+               cancel = build_local(t, branch, &len, CANCEL, CANCEL_LEN, &t->to);
+       }
        if (!cancel) {
                LOG(L_ERR, "ERROR: attempt to build a CANCEL failed\n");
                return -1;
index b693f56..65dadf4 100644 (file)
  *  2007-03-15  TMCB_ONSEND hooks added (andrei)
  *  2007-05-02  added t_forward_cancel(unmatched_cancel) (andrei)
  *  2007-05-24  added TMCB_E2ECANCEL_IN hook support (andrei)
+ *  2007-05-28: e2e_cancel_branch() constructs the CANCEL from the
+ *              outgoing INVITE instead of applying the lumps to the
+ *              incomming one. (it can be disabled with reparse_invite=0) (Miklos)
+ *              t_relay_cancel() introduced -- can be used to relay CANCELs
+ *              at the beginning of the script. (Miklos)
  */
 
 #include "defs.h"
@@ -440,9 +445,21 @@ int e2e_cancel_branch( struct sip_msg *cancel_msg, struct cell *t_cancel,
        */
 
        /* print */
-       shbuf=print_uac_request( t_cancel, cancel_msg, branch, 
+       if (reparse_invite) {
+               /* buffer is built localy from the INVITE which was sent out */
+               if (cancel_msg->add_rm || cancel_msg->body_lumps) {
+                       LOG(L_WARN, "WARNING: e2e_cancel_branch: CANCEL is built locally, "
+                       "thus lumps are not applied to the message!\n");
+               }
+               shbuf=build_local_reparse( t_invite, branch, &len, CANCEL, CANCEL_LEN, &t_invite->to);
+
+       } else {
+               /* buffer is constructed from the received CANCEL with applying lumps */
+               shbuf=print_uac_request( t_cancel, cancel_msg, branch, 
                                                        &t_invite->uac[branch].uri, &len, 
                                                        &t_invite->uac[branch].request.dst);
+       }
+
        if (!shbuf) {
                LOG(L_ERR, "ERROR: e2e_cancel_branch: printing e2e cancel failed\n");
                ret=ser_error=E_OUT_OF_MEM;
@@ -946,7 +963,52 @@ end:
        return ret;
 }
 
+/* Relays a CANCEL request if a corresponding INVITE transaction
+ * can be found. The function is supposed to be used at the very
+ * beginning of the script with reparse_invite=1 module parameter.
+ *
+ * return value:
+ *    0: the CANCEL was successfully relayed
+ *       (or error occured but reply cannot be sent) => DROP
+ *    1: no corresponding INVITE transaction exisis
+ *   <0: corresponding INVITE transaction exisis but error occured
+ */
+int t_relay_cancel(struct sip_msg* p_msg)
+{
+       struct cell* t_invite;
+       struct cell* t;
+       int ret;
+       int new_tran;
 
+       t_invite=t_lookupOriginalT(  p_msg );
+       if (t_invite!=T_NULL_CELL) {
+               /* create cancel transaction */
+               new_tran=t_newtran(p_msg);
+               if (new_tran<=0 && new_tran!=E_SCRIPT){
+                       if (new_tran==0)
+                               /* retransmission => DROP,
+                               t_newtran() takes care about it */
+                               ret=0;
+                       else
+                               /* some error => return it or DROP */
+                               ret=(ser_error==E_BAD_VIA && reply_to_via) ? 0: new_tran;
+                       UNREF(t_invite);
+                       goto end;
+               }
+               t=get_t();
+               e2e_cancel( p_msg, t, t_invite );
+               UNREF(t_invite);
+               /* return 0 to stop the script processing */
+               ret=0;
+               goto end;
+
+       } else {
+               /* no corresponding INVITE trasaction found */
+               ret=1;
+       }
+end:
+       return ret;
+}
 
 /* WARNING: doesn't work from failure route (deadlock, uses t_relay_to which
  *  is failure route unsafe) */
index 9b074e1..f7521bf 100644 (file)
@@ -73,6 +73,7 @@ int t_forward_cancel(struct sip_msg* p_msg , struct proxy_l * proxy,
 int t_forward_ack( struct sip_msg* p_msg );
 int t_send_branch( struct cell *t, int branch, struct sip_msg* p_msg ,
                                        struct proxy_l * proxy, int lock_replies);
+int t_relay_cancel(struct sip_msg* p_msg);
 
 
 #endif
index f2803db..d23616a 100644 (file)
@@ -44,6 +44,8 @@
  *              is found (andrei)
  * 2007-03-15  build_dls_ack: removed next_hop and replaced by dst to avoid
  *               resolving nexthop twice (andrei)
+ * 2007-05-28: build_local_reparse() is introdued: it uses the outgoing
+ *             INVITE as a source to construct a CANCEL or ACK (Miklos)
  */
 
 #include "defs.h"
@@ -59,6 +61,7 @@
 #include "../../ut.h"
 #include "../../parser/msg_parser.h"
 #include "../../parser/contact/parse_contact.h"
+#include "lw_parser.h"
 #include "t_msgbuilder.h"
 #include "uac.h"
 #ifdef USE_DNS_FAILOVER
                (_p)+=(_str).len;  \
        } while(0);
 
+str ac_extra_hdrs = STR_STATIC_INIT("");
+int reparse_invite = 1;
+
+
 /* Build a local request based on a previous request; main
    customers of this function are local ACK and local CANCEL
  */
@@ -187,6 +194,159 @@ error:
        return NULL;
 }
 
+/* Re-parsing version of build_local() function:
+ * it builds a local CANCEL or ACK (for non-200 response) request based on
+ * the previous INVITE which was sent out.
+ *
+ * Can not be used to build other type of requests!
+ */
+char *build_local_reparse(struct cell *Trans,unsigned int branch,
+       unsigned int *len, char *method, int method_len, str *to)
+{
+       char    *invite_buf, *invite_buf_end;
+       char    *cancel_buf;
+       char    *s, *s1, *d;    /* source and destination buffers */
+       short   invite_len;
+       enum _hdr_types_t       hf_type;
+       int     first_via, to_len;
+
+       invite_buf = Trans->uac[branch].request.buffer;
+       invite_len = Trans->uac[branch].request.buffer_len;
+
+       if (!invite_buf || !invite_len) {
+               LOG(L_ERR, "ERROR: build_local_reparse: INVITE is missing\n");
+               goto error;
+       }
+       if ((*invite_buf != 'I') && (*invite_buf != 'i')) {
+               LOG(L_ERR, "ERROR: build_local_reparse: trying to call build_local_reparse() for a non-INVITE request?\n");
+               goto error;
+       }
+       invite_buf_end = invite_buf + invite_len;
+       s = invite_buf;
+
+       /* Allocate memory for the new message.
+       The new request will be smaller than the INVITE, so the same size is enough.
+       I just extend it with the length of new To HF to be sure.
+       Ugly, but we avoid lots of checks and memory allocations this way */
+       to_len = to ? to->len : 0;
+       cancel_buf = shm_malloc(sizeof(char)*(invite_len + to_len));
+       if (!cancel_buf)
+       {
+               LOG(L_ERR, "ERROR: build_local_reparse: cannot allocate shared memory\n");
+               goto error;
+       }
+       d = cancel_buf;
+
+       /* method name + space */
+       append_mem_block(d, method, method_len);
+       *d = ' ';
+       d++;
+       /* skip "INVITE " and copy the rest of the line including CRLF */
+       s += 7;
+       s1 = s;
+       s = eat_line(s, invite_buf_end - s);
+       append_mem_block(d, s1, s - s1);
+
+       /* check every header field name,
+       we must exclude and modify some of the headers */
+       first_via = 1;
+       while (s < invite_buf_end) {
+               s1 = s;
+               if ((*s == '\n') || (*s == '\r')) {
+                       /* end of SIP msg */
+                       hf_type = HDR_EOH_T;
+               } else {
+                       /* parse HF name */
+                       s = lw_get_hf_name(s, invite_buf_end,
+                                               &hf_type);
+               }
+
+               switch(hf_type) {
+                       case HDR_CSEQ_T:
+                               /* find the method name and replace it */
+                               while ((s < invite_buf_end)
+                                       && ((*s == ':') || (*s == ' ') || (*s == '\t') || ((*s >= '0') && (*s <= '9')))
+                                       ) s++;
+                               append_mem_block(d, s1, s - s1);
+                               append_mem_block(d, method, method_len);
+                               append_mem_block(d, CRLF, CRLF_LEN);
+                               s = lw_next_line(s, invite_buf_end);
+                               break;
+
+                       case HDR_VIA_T:
+                               s = lw_next_line(s, invite_buf_end);
+                               if (first_via) {
+                                       /* copy hf */
+                                       append_mem_block(d, s1, s - s1);
+                                       first_via = 0;
+                               } /* else skip this line, we need olny the first via */
+                               break;
+
+                       case HDR_TO_T:
+                               if (to_len == 0) {
+                                       /* there is no To tag required, just copy paste the header */
+                                       s = lw_next_line(s, invite_buf_end);
+                                       append_mem_block(d, s1, s - s1);
+                               } else {
+                                       /* use the given To HF instead of the original one */
+                                       append_mem_block(d, to->s, to->len);
+                                       /* move the pointer to the next line */
+                                       s = lw_next_line(s, invite_buf_end);
+                               }
+                               break;
+
+                       case HDR_FROM_T:
+                       case HDR_CALLID_T:
+                       case HDR_ROUTE_T:
+                       case HDR_MAXFORWARDS_T:
+                               /* copy hf */
+                               s = lw_next_line(s, invite_buf_end);
+                               append_mem_block(d, s1, s - s1);
+                               break;
+
+                       case HDR_REQUIRE_T:
+                       case HDR_PROXYREQUIRE_T:
+                               /* skip this line */
+                               s = lw_next_line(s, invite_buf_end);
+                               break;
+
+                       case HDR_CONTENTLENGTH_T:
+                               /* copy hf name with 0 value */
+                               append_mem_block(d, s1, s - s1);
+                               append_mem_block(d, ": 0" CRLF, 3 + CRLF_LEN);
+                               /* move the pointer to the next line */
+                               s = lw_next_line(s, invite_buf_end);
+                               break;
+
+                       case HDR_EOH_T:
+                               /* end of SIP message found */
+                               append_mem_block(d, CRLF, CRLF_LEN);
+                               *len = d - cancel_buf;
+                               /* LOG(L_DBG, "DBG: build_local: %.*s\n", *len, cancel_buf); */
+                               return cancel_buf;
+
+                       default:
+                               s = lw_next_line(s, invite_buf_end);
+
+                               if (ac_extra_hdrs.len
+                               && (s1 + ac_extra_hdrs.len < invite_buf_end)
+                               && (strncasecmp(s1, ac_extra_hdrs.s, ac_extra_hdrs.len) == 0)) {
+                                       append_mem_block(d, s1, s - s1);
+                               } /* else skip this line */
+                               break;
+               }
+       }
+
+       /* HDR_EOH_T was not found in the buffer, the message is corrupt */
+       LOG(L_ERR, "ERROR: build_local_reparse: HDR_EOH_T was not found\n");
+
+       shm_free(cancel_buf);
+error:
+       LOG(L_ERR, "ERROR: build_local_reparse: cannot build %.*s request\n", method_len, method);
+       return NULL;
+
+}
+
 
 struct rte {
        rr_t* ptr;
index 7d8eb7f..62d2722 100644 (file)
                (_d) += (_len);\
        }while(0);
 
+extern str ac_extra_hdrs;
+extern int reparse_invite;
+
 char *build_local(struct cell *Trans, unsigned int branch,
        unsigned int *len, char *method, int method_len, str *to);
 
+char *build_local_reparse(struct cell *Trans, unsigned int branch,
+       unsigned int *len, char *method, int method_len, str *to);
+
 char *build_uac_request(  str msg_type, str dst, str from,
        str fromtag, int cseq, str callid, str headers, 
        str body, int branch,
index ad90d26..cdbaa0f 100644 (file)
@@ -82,6 +82,9 @@
  * 2007-03-15  build_local_ack: removed next_hop and replaced with dst to 
  *              avoid resolving next_hop twice
  *              added TMCB_ONSEND callbacks support for replies & ACKs (andrei)
+ * 2007-05-28: build_ack() constructs the ACK from the
+ *             outgoing INVITE instead of the incomming one.
+ *             (it can be disabled with reparse_invite=0) (Miklos)
  *
  */
 
@@ -333,8 +336,16 @@ static char *build_ack(struct sip_msg* rpl,struct cell *trans,int branch,
     }
        to.s=rpl->to->name.s;
        to.len=rpl->to->len;
-    return build_local( trans, branch, ret_len,
-        ACK, ACK_LEN, &to );
+
+       if (reparse_invite) {
+               /* build the ACK from the INVITE which was sent out */
+               return build_local_reparse( trans, branch, ret_len,
+                                       ACK, ACK_LEN, &to );
+       } else {
+               /* build the ACK from the reveived INVITE */
+               return build_local( trans, branch, ret_len,
+                                       ACK, ACK_LEN, &to );
+       }
 }
 
 
index 60eeadc..608749a 100644 (file)
@@ -80,6 +80,8 @@
  *  2006-09-28  added t_branch_replied, t_branch_timeout, t_any_replied, 
  *               t_any_timeout, t_is_canceled (andrei)
  *  2006-10-16  added a new param.: aggregate challenges (andrei)
+ *  2007-05-28  two new params: reparse_invite, ac_extra_hdrs
+ *              added w_t_relay_cancel() (Miklos)
  */
 
 
@@ -180,6 +182,7 @@ inline static int w_t_forward_nonack_tcp(struct sip_msg* msg, char* str,char*);
 inline static int w_t_forward_nonack_tls(struct sip_msg* msg, char* str,char*);
 #endif
 inline static int w_t_forward_nonack_to(struct sip_msg* msg, char* str,char*);
+inline static int w_t_relay_cancel(struct sip_msg *p_msg, char *_foo, char *_bar);
 inline static int w_t_on_negative(struct sip_msg* msg, char *go_to, char *foo);
 inline static int w_t_on_branch(struct sip_msg* msg, char *go_to, char *foo);
 inline static int w_t_on_reply(struct sip_msg* msg, char *go_to, char *foo );
@@ -261,6 +264,8 @@ static cmd_export_t cmds[]={
 #endif
        {"t_forward_nonack_to", w_t_forward_nonack_to,  2, fixup_proto_hostport2proxy,
                        REQUEST_ROUTE},
+       {"t_relay_cancel",     w_t_relay_cancel,        0, 0,
+                       REQUEST_ROUTE},
        {"t_on_failure",       w_t_on_negative,         1, fixup_on_failure,
                        REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE },
        {"t_on_reply",         w_t_on_reply,            1, fixup_on_reply,
@@ -338,6 +343,8 @@ static param_export_t params[]={
        {"unmatched_cancel",    PARAM_INT, &unmatched_cancel                     },
        {"default_code",        PARAM_INT, &default_code                         },
        {"default_reason",      PARAM_STR, &default_reason                       },
+       {"reparse_invite",      PARAM_INT, &reparse_invite                       },
+       {"ac_extra_hdrs",       PARAM_STR, &ac_extra_hdrs                        },
        {0,0,0}
 };
 
@@ -1123,6 +1130,21 @@ inline static int w_t_relay( struct sip_msg  *p_msg ,
        return 0;
 }
 
+/* relays CANCEL at the beginning of the script */
+inline static int w_t_relay_cancel( struct sip_msg  *p_msg ,
+                                               char *_foo, char *_bar)
+{
+       if (p_msg->REQ_METHOD!=METHOD_CANCEL)
+               return 1;
+
+       /* it makes no sense to use this function without reparse_invite=1 */
+       if (!reparse_invite)
+               LOG(L_WARN, "WARNING: t_relay_cancel is probably used with "
+                       "wrong configuration, check the readme for details\n");
+
+       return t_relay_cancel(p_msg);
+}
+
 /* set fr_inv_timeout & or fr_timeout; 0 means: use the default value */
 static int t_set_fr_all(struct sip_msg* msg, char* p1, char* p2)
 {