Browse code

Merge remote branch 'origin/andrei/cancel_reason'

CANCEL Reason support, according to RFC 3326.
The Reason headers are added both for CANCELs generated due to a
received final reply and hop by hop CANCELs generated because of
a received CANCEL. Both cases can be turned on/off individually.

* origin/andrei/cancel_reason:
tm: no reason modparams if compiled with no cancelr. support
tm: option to compile without reason support
tm: docs: reason header modparams and script function
tm: improved Reason support for E2E CANCELs
tm: fix Reason generation for on-the-fly branch CANCELs
tm: CANCEL reason header on/off switches
tm: Reason header copy for received CANCELs
tm: Reason header generation for local CANCELs

Andrei Pelinescu-Onciul authored on 13/08/2010 19:44:36
Showing 19 changed files
... ...
@@ -109,6 +109,23 @@ modules:
109 109
             increase dramatically especially for large number of
110 110
             connections (>1000).
111 111
 tm:
112
+   - reason header support (RFC3326) both for CANCELs generated due to a
113
+      received final reply and for hop by hop CANCELs generated because of a
114
+      received CANCEL.
115
+      E.g.: reason header added for a CANCEL generated after a 200 reply was
116
+            received on one of the branches "Reason: SIP;cause=200".
117
+      The reason header support can be turned on/off using either tm
118
+      module parameters or in the end to end CANCEL case also on a per
119
+      transaction basis, using a script function:
120
+       local_cancel_reason = 0 | 1 (default 1/on) - turns on adding reason
121
+         headers for CANCELs generated due to a final reply. Can be changed
122
+         at runtime.
123
+       e2e_cancel_reason = 0 | 1 (default 1/on) - turns on copying reason
124
+         headers from a received end to end CANCEL (the generated hop by hop
125
+         CANCELs will have the same reason headers as the received CANCEL).
126
+         Can be changed at runtime.
127
+       t_set_no_e2e_cancel_reason(0|1) - enable/disable cancel reason 
128
+         header copying on a per transaction basis (0 - enable, 1 disable).
112 129
    - t_reply() can be used both from the main/core onreply_route{} and tm
113 130
      onreply_route[...]{}s.
114 131
 
... ...
@@ -8,9 +8,9 @@ Juha Heinanen
8 8
 
9 9
    <jh@tutpro.com>
10 10
 
11
-   Copyright © 2003 FhG FOKUS
11
+   Copyright � 2003 FhG FOKUS
12 12
 
13
-   Copyright © 2008 Juha Heinanen
13
+   Copyright � 2008 Juha Heinanen
14 14
    Revision History
15 15
    Revision $Revision$ $Date$
16 16
      __________________________________________________________________
... ...
@@ -57,6 +57,8 @@ Juha Heinanen
57 57
         1.4.35. disable_6xx_block (integer)
58 58
         1.4.36. local_ack_mode (integer)
59 59
         1.4.37. failure_reply_mode (integer)
60
+        1.4.38. local_cancel_reason (boolean)
61
+        1.4.39. e2e_cancel_reason (boolean)
60 62
 
61 63
    1.5. Functions
62 64
 
... ...
@@ -105,6 +107,7 @@ Juha Heinanen
105 105
         1.5.35. t_set_disable_failover(0|1)
106 106
         1.5.36. t_replicate(params)
107 107
         1.5.37. t_relay_to(proxy, flags)
108
+        1.5.38. t_set_no_e2e_cancel_reason(0|1)
108 109
 
109 110
    1.6. TM Module API
110 111
 
... ...
@@ -174,7 +177,7 @@ Juha Heinanen
174 174
 Note
175 175
 
176 176
    Several Kamailio (OpenSER) TM module functionalities are now
177
-   implemented in the TMX module: “modules_k/tmx”. Check it to see if what
177
+   implemented in the TMX module: "modules_k/tmx". Check it to see if what
178 178
    you are looking for is there.
179 179
 
180 180
 1.2. Serial Forking Based on Q Value
... ...
@@ -1131,6 +1134,42 @@ modparam("tm", "local_ack_mode", 1)
1131 1131
 modparam("tm", "failure_reply_mode", 3)
1132 1132
 ...
1133 1133
 
1134
+1.4.38. local_cancel_reason (boolean)
1135
+
1136
+   Enables/disables adding reason headers (RFC 3326) for CANCELs generated
1137
+   due to receiving a final reply. The reason header added will look like:
1138
+   "Reason: SIP;cause=<final_reply_code>".
1139
+
1140
+   Default value is 1 (enabled).
1141
+
1142
+   Can be set at runtime, e.g.:
1143
+        $ sercmd cfg.set_now_int tm local_cancel_reason 0
1144
+
1145
+   See also: e2e_cancel_reason.
1146
+
1147
+   Example 38. Set local_cancel_reason parameter
1148
+...
1149
+modparam("tm", "local_cancel_reason", "0")
1150
+...
1151
+
1152
+1.4.39. e2e_cancel_reason (boolean)
1153
+
1154
+   Enables/disables adding reason headers (RFC 3326) for CANCELs generated
1155
+   due to a received CANCEL. If enabled the reason headers from received
1156
+   CANCELs will be copied into the generated hop-by-hop CANCELs.
1157
+
1158
+   Default value is 1 (enabled).
1159
+
1160
+   Can be changed at runtime, e.g.:
1161
+        $ sercmd cfg.set_now_int tm e2e_cancel_reason 0
1162
+
1163
+   See also: t_set_no_e2e_cancel_reason() and local_cancel_reason.
1164
+
1165
+   Example 39. Set e2e_cancel_reason parameter
1166
+...
1167
+modparam("tm", "e2e_cancel_reason", "0")
1168
+...
1169
+
1134 1170
 1.5. Functions
1135 1171
 
1136 1172
    Revision History
... ...
@@ -1156,7 +1195,7 @@ t_relay_to_sctp(ip, port) t_relay_to_sctp()
1156 1156
    derived from the message uri (using sip sepcific DNS lookups), but with
1157 1157
    the protocol corresponding to the function name.
1158 1158
 
1159
-   Example 38. t_relay_to_udp usage
1159
+   Example 40. t_relay_to_udp usage
1160 1160
 ...
1161 1161
 if (src_ip==10.0.0.0/8)
1162 1162
         t_relay_to_udp("1.2.3.4", "5060"); # sent to 1.2.3.4:5060 over udp
... ...
@@ -1183,7 +1222,7 @@ else
1183 1183
    Returns a negative value on failure--you may still want to send a
1184 1184
    negative reply upstream statelessly not to leave upstream UAC in lurch.
1185 1185
 
1186
-   Example 39. t_relay usage
1186
+   Example 41. t_relay usage
1187 1187
 ...
1188 1188
 if (!t_relay())
1189 1189
 {
... ...
@@ -1212,7 +1251,7 @@ if (!t_relay())
1212 1212
    Meaning of the parameters is as follows:
1213 1213
      * failure_route - Failure route block to be called.
1214 1214
 
1215
-   Example 40. t_on_failure usage
1215
+   Example 42. t_on_failure usage
1216 1216
 ...
1217 1217
 route {
1218 1218
     t_on_failure("1");
... ...
@@ -1238,7 +1277,7 @@ failure_route[1] {
1238 1238
    Meaning of the parameters is as follows:
1239 1239
      * onreply_route - Onreply route block to be called.
1240 1240
 
1241
-   Example 41. t_on_reply usage
1241
+   Example 43. t_on_reply usage
1242 1242
 ...
1243 1243
 loadmodule "/usr/local/lib/ser/modules/nathelper.so"
1244 1244
 ...
... ...
@@ -1270,7 +1309,7 @@ es');
1270 1270
    Meaning of the parameters is as follows:
1271 1271
      * branch_route - branch route block to be called.
1272 1272
 
1273
-   Example 42. t_on_branch usage
1273
+   Example 44. t_on_branch usage
1274 1274
 ...
1275 1275
 route {
1276 1276
         t_on_branch("1");
... ...
@@ -1288,7 +1327,7 @@ branch_route[1] {
1288 1288
    Similarly to t_fork_to, it extends destination set by a new entry. The
1289 1289
    difference is that current URI is taken as new entry.
1290 1290
 
1291
-   Example 43. append_branch usage
1291
+   Example 45. append_branch usage
1292 1292
 ...
1293 1293
 set_user("john");
1294 1294
 t_fork();
... ...
@@ -1303,7 +1342,7 @@ t_relay();
1303 1303
    the only way a script can add a new transaction in an atomic way.
1304 1304
    Typically, it is used to deploy a UAS.
1305 1305
 
1306
-   Example 44. t_newtran usage
1306
+   Example 46. t_newtran usage
1307 1307
 ...
1308 1308
 if (t_newtran()) {
1309 1309
     log("UAS logic");
... ...
@@ -1322,7 +1361,7 @@ if (t_newtran()) {
1322 1322
      * code - Reply code number.
1323 1323
      * reason_phrase - Reason string.
1324 1324
 
1325
-   Example 45. t_reply usage
1325
+   Example 47. t_reply usage
1326 1326
 ...
1327 1327
 t_reply("404", "Not found");
1328 1328
 ...
... ...
@@ -1335,7 +1374,7 @@ t_reply("404", "Not found");
1335 1335
    none was found. However this is safely (atomically) done using
1336 1336
    t_newtran.
1337 1337
 
1338
-   Example 46. t_lookup_request usage
1338
+   Example 48. t_lookup_request usage
1339 1339
 ...
1340 1340
 if (t_lookup_request()) {
1341 1341
     ...
... ...
@@ -1346,7 +1385,7 @@ if (t_lookup_request()) {
1346 1346
 
1347 1347
    Retransmits a reply sent previously by UAS transaction.
1348 1348
 
1349
-   Example 47. t_retransmit_reply usage
1349
+   Example 49. t_retransmit_reply usage
1350 1350
 ...
1351 1351
 t_retransmit_reply();
1352 1352
 ...
... ...
@@ -1356,7 +1395,7 @@ t_retransmit_reply();
1356 1356
    Remove transaction from memory (it will be first put on a wait timer to
1357 1357
    absorb delayed messages).
1358 1358
 
1359
-   Example 48. t_release usage
1359
+   Example 50. t_release usage
1360 1360
 ...
1361 1361
 t_release();
1362 1362
 ...
... ...
@@ -1371,7 +1410,7 @@ t_forward_nonack_tls(ip, port) t_forward_nonack_sctp(ip, port)
1371 1371
      * ip - IP address where the message should be sent.
1372 1372
      * port - Port number.
1373 1373
 
1374
-   Example 49. t_forward_nonack usage
1374
+   Example 51. t_forward_nonack usage
1375 1375
 ...
1376 1376
 t_forward_nonack("1.2.3.4", "5060");
1377 1377
 ...
... ...
@@ -1394,7 +1433,7 @@ t_forward_nonack("1.2.3.4", "5060");
1394 1394
 
1395 1395
    See also: fr_timer, fr_inv_timer, t_reset_fr().
1396 1396
 
1397
-   Example 50. t_set_fr usage
1397
+   Example 52. t_set_fr usage
1398 1398
 ...
1399 1399
 route {
1400 1400
         t_set_fr(10000); # set only fr invite timeout to 10s
... ...
@@ -1421,7 +1460,7 @@ branch_route[1] {
1421 1421
 
1422 1422
    See also: fr_timer, fr_inv_timer, t_set_fr.
1423 1423
 
1424
-   Example 51. t_reset_fr usage
1424
+   Example 53. t_reset_fr usage
1425 1425
 ...
1426 1426
 route {
1427 1427
 ...
... ...
@@ -1447,7 +1486,7 @@ route {
1447 1447
 
1448 1448
    See also: max_inv_lifetime, max_noninv_lifetime, t_reset_max_lifetime.
1449 1449
 
1450
-   Example 52. t_set_max_lifetime usage
1450
+   Example 54. t_set_max_lifetime usage
1451 1451
 ...
1452 1452
 route {
1453 1453
     if (src_ip=1.2.3.4)
... ...
@@ -1469,7 +1508,7 @@ route {
1469 1469
 
1470 1470
    See also: max_inv_lifetime, max_noninv_lifetime, t_set_max_lifetime.
1471 1471
 
1472
-   Example 53. t_reset_max_lifetime usage
1472
+   Example 55. t_reset_max_lifetime usage
1473 1473
 ...
1474 1474
 route {
1475 1475
 ...
... ...
@@ -1507,7 +1546,7 @@ route {
1507 1507
 
1508 1508
    See also: retr_timer1, retr_timer2, t_reset_retr().
1509 1509
 
1510
-   Example 54. t_set_retr usage
1510
+   Example 56. t_set_retr usage
1511 1511
 ...
1512 1512
 route {
1513 1513
         t_set_retr(250, 0); # set only T1 to 250 ms
... ...
@@ -1534,7 +1573,7 @@ branch_route[1] {
1534 1534
 
1535 1535
    See also: retr_timer1, retr_timer2, t_set_retr.
1536 1536
 
1537
-   Example 55. t_reset_retr usage
1537
+   Example 57. t_reset_retr usage
1538 1538
 ...
1539 1539
 route {
1540 1540
 ...
... ...
@@ -1550,7 +1589,7 @@ route {
1550 1550
 
1551 1551
    See also: auto_inv_100.
1552 1552
 
1553
-   Example 56. t_set_auto_inv_100 usage
1553
+   Example 58. t_set_auto_inv_100 usage
1554 1554
 ...
1555 1555
 route {
1556 1556
 ...
... ...
@@ -1564,7 +1603,7 @@ route {
1564 1564
    Returns true if the failure route is executed for a branch that did
1565 1565
    timeout. It can be used only from the failure_route.
1566 1566
 
1567
-   Example 57. t_branch_timeout usage
1567
+   Example 59. t_branch_timeout usage
1568 1568
 ...
1569 1569
 failure_route[0]{
1570 1570
         if (t_branch_timeout()){
... ...
@@ -1579,7 +1618,7 @@ failure_route[0]{
1579 1579
    receive at least one reply in the past (the "current" reply is not
1580 1580
    taken into account). It can be used only from the failure_route.
1581 1581
 
1582
-   Example 58. t_branch_replied usage
1582
+   Example 60. t_branch_replied usage
1583 1583
 ...
1584 1584
 failure_route[0]{
1585 1585
         if (t_branch_timeout()){
... ...
@@ -1596,7 +1635,7 @@ failure_route[0]{
1596 1596
    Returns true if at least one of the current transactions branches did
1597 1597
    timeout.
1598 1598
 
1599
-   Example 59. t_any_timeout usage
1599
+   Example 61. t_any_timeout usage
1600 1600
 ...
1601 1601
 failure_route[0]{
1602 1602
         if (!t_branch_timeout()){
... ...
@@ -1613,7 +1652,7 @@ failure_route[0]{
1613 1613
    receive some reply in the past. If called from a failure or onreply
1614 1614
    route, the "current" reply is not taken into account.
1615 1615
 
1616
-   Example 60. t_any_replied usage
1616
+   Example 62. t_any_replied usage
1617 1617
 ...
1618 1618
 onreply_route[0]{
1619 1619
         if (!t_any_replied()){
... ...
@@ -1627,7 +1666,7 @@ onreply_route[0]{
1627 1627
    Returns true if "code" is the final reply received (or locally
1628 1628
    generated) in at least one of the current transactions branches.
1629 1629
 
1630
-   Example 61. t_grep_status usage
1630
+   Example 63. t_grep_status usage
1631 1631
 ...
1632 1632
 onreply_route[0]{
1633 1633
         if (t_grep_status("486")){
... ...
@@ -1640,7 +1679,7 @@ onreply_route[0]{
1640 1640
 
1641 1641
    Returns true if the current transaction was canceled.
1642 1642
 
1643
-   Example 62. t_is_canceled usage
1643
+   Example 64. t_is_canceled usage
1644 1644
 ...
1645 1645
 failure_route[0]{
1646 1646
         if (t_is_canceled()){
... ...
@@ -1654,7 +1693,7 @@ failure_route[0]{
1654 1654
    Returns true if the current transaction has already been expired, i.e.
1655 1655
    the max_inv_lifetime/max_noninv_lifetime interval has already elapsed.
1656 1656
 
1657
-   Example 63. t_is_expired usage
1657
+   Example 65. t_is_expired usage
1658 1658
 ...
1659 1659
 failure_route[0]{
1660 1660
         if (t_is_expired()){
... ...
@@ -1675,7 +1714,7 @@ failure_route[0]{
1675 1675
    CANCELs were successfully sent to the pending branches, true if the
1676 1676
    INVITE was not found, and false in case of any error.
1677 1677
 
1678
-   Example 64. t_relay_cancel usage
1678
+   Example 66. t_relay_cancel usage
1679 1679
 if (method == CANCEL) {
1680 1680
         if (!t_relay_cancel()) {  # implicit drop if relaying was successful,
1681 1681
                                   # nothing to do
... ...
@@ -1702,7 +1741,7 @@ if (method == CANCEL) {
1702 1702
    overwritten with the flags of the INVITE. isflagset() can be used to
1703 1703
    check the flags of the previously forwarded INVITE in this case.
1704 1704
 
1705
-   Example 65. t_lookup_cancel usage
1705
+   Example 67. t_lookup_cancel usage
1706 1706
 if (method == CANCEL) {
1707 1707
         if (t_lookup_cancel()) {
1708 1708
                 log("INVITE transaction exists");
... ...
@@ -1732,7 +1771,7 @@ if (method == CANCEL) {
1732 1732
    Dropping replies works only if a new branch is added to the
1733 1733
    transaction, or it is explicitly replied in the script!
1734 1734
 
1735
-   Example 66. t_drop_replies() usage
1735
+   Example 68. t_drop_replies() usage
1736 1736
 ...
1737 1737
 failure_route[0]{
1738 1738
         if (t_check_status("5[0-9][0-9]")){
... ...
@@ -1763,7 +1802,7 @@ failure_route[0]{
1763 1763
    The transaction must be created by t_newtran() before calling
1764 1764
    t_save_lumps().
1765 1765
 
1766
-   Example 67. t_save_lumps() usage
1766
+   Example 69. t_save_lumps() usage
1767 1767
 route {
1768 1768
         ...
1769 1769
         t_newtran();
... ...
@@ -1833,7 +1872,7 @@ failure_route[1] {
1833 1833
 
1834 1834
    This function can be used from REQUEST_ROUTE.
1835 1835
 
1836
-   Example 68. t_load_contacts usage
1836
+   Example 70. t_load_contacts usage
1837 1837
 ...
1838 1838
 if (!t_load_contacts()) {
1839 1839
         sl_send_reply("500", "Server Internal Error - Cannot load contacts");
... ...
@@ -1874,7 +1913,7 @@ if (!t_load_contacts()) {
1874 1874
    anymore set. Based on that test, you can then use t_set_fr() function
1875 1875
    to set timers according to your needs.
1876 1876
 
1877
-   Example 69. t_next_contacts usage
1877
+   Example 71. t_next_contacts usage
1878 1878
 ...
1879 1879
 # First call after t_load_contacts() when transaction does not exist yet
1880 1880
 # and contacts should be available
... ...
@@ -1938,7 +1977,7 @@ Note
1938 1938
 
1939 1939
    See also: t_lookup_request(), t_lookup_cancel().
1940 1940
 
1941
-   Example 70. t_check_trans usage
1941
+   Example 72. t_check_trans usage
1942 1942
 if ( method == "CANCEL" && !t_check_trans())
1943 1943
         sl_reply("403", "cancel out of the blue forbidden");
1944 1944
 # note: in this example t_check_trans() can be replaced by t_lookup_cancel()
... ...
@@ -1953,7 +1992,7 @@ if ( method == "CANCEL" && !t_check_trans())
1953 1953
 
1954 1954
    See also: disable_6xx_block.
1955 1955
 
1956
-   Example 71. t_set_disable_6xx usage
1956
+   Example 73. t_set_disable_6xx usage
1957 1957
 ...
1958 1958
 route {
1959 1959
 ...
... ...
@@ -1968,7 +2007,7 @@ route {
1968 1968
 
1969 1969
    See also: use_dns_failover.
1970 1970
 
1971
-   Example 72. t_set_disable_failover usage
1971
+   Example 74. t_set_disable_failover usage
1972 1972
 ...
1973 1973
 route {
1974 1974
 ...
... ...
@@ -1999,7 +2038,7 @@ route {
1999 1999
      * hostport - address in "host:port" format. It can be given via an
2000 2000
        AVP.
2001 2001
 
2002
-   Example 73. t_replicate usage
2002
+   Example 75. t_replicate usage
2003 2003
 ...
2004 2004
 # sent to 1.2.3.4:5060 over tcp
2005 2005
 t_replicate("sip:1.2.3.4:5060;transport=tcp");
... ...
@@ -2034,7 +2073,7 @@ t_replicate_to_udp("1.2.3.4", "5060");
2034 2034
             effect anymore).
2035 2035
           + 0x04 - disable dns failover.
2036 2036
 
2037
-   Example 74. t_replicate usage
2037
+   Example 76. t_replicate usage
2038 2038
 ...
2039 2039
 # sent to 1.2.3.4:5060 over tcp
2040 2040
 t_relay_to("tcp:1.2.3.4:5060");
... ...
@@ -2046,6 +2085,27 @@ t_relay_to("tls:1.2.3.4");
2046 2046
 t_relay_to("0x01");
2047 2047
 ...
2048 2048
 
2049
+1.5.38.  t_set_no_e2e_cancel_reason(0|1)
2050
+
2051
+   Enables/disables reason header (RFC 3326) copying from the triggering
2052
+   received CANCEL to the generated hop-by-hop CANCEL. 0 enables and 1
2053
+   disables.
2054
+
2055
+   It overrides the e2e_cancel_reason setting (module parameter) for the
2056
+   current transaction.
2057
+
2058
+   See also: e2e_cancel_reason.
2059
+
2060
+   Example 77. t_set_no_e2e_cancel_reason usage
2061
+...
2062
+route {
2063
+...
2064
+        if (src_ip!=10.0.0.0/8) #  don't trust CANCELs from the outside
2065
+                t_set_no_e2e_cancel_reason(1); # turn off CANCEL reason header c
2066
+opying
2067
+...
2068
+}
2069
+
2049 2070
 1.6. TM Module API
2050 2071
 
2051 2072
    Revision History
... ...
@@ -24,10 +24,9 @@
24 24
  *  2008-02-05	adapting tm module for the configuration framework (Miklos)
25 25
  */
26 26
 
27
-/*!
28
- * \file 
29
- * \brief TM :: Configuration
30
- * \ingroup tm
27
+/** TM :: Runtime configuration variables.
28
+ * @file
29
+ * @ingroup tm
31 30
  */
32 31
 
33 32
 
... ...
@@ -94,7 +93,11 @@ struct cfg_group_tm	default_tm_cfg = {
94 94
 	1,	/* cancel_b_method used for e2e and 6xx cancels*/
95 95
 	1,	/* reparse_on_dns_failover */
96 96
 	0, /* disable_6xx, by default off */
97
-	0  /* local_ack_mode, default 0 (rfc3261 conformant) */
97
+	0,  /* local_ack_mode, default 0 (rfc3261 conformant) */
98
+	1, /* local_cancel_reason -- add Reason header to locally generated
99
+		  CANCELs; on by default */
100
+	1  /* e2e_cancel_reason -- copy the Reason headers from incoming CANCELs
101
+		  into the corresp. hop-by-hop CANCELs, on by default */
98 102
 };
99 103
 
100 104
 void	*tm_cfg = &default_tm_cfg;
... ...
@@ -196,5 +199,11 @@ cfg_def_t	tm_cfg_def[] = {
196 196
 		" it is not set to 0 but allows dealing with NATed contacts in some "
197 197
 		"simple cases)"
198 198
 		},
199
+	{"local_cancel_reason",	CFG_VAR_INT | CFG_ATOMIC,	0, 1, 0, 0,
200
+		"if set to 1, a Reason header is added to locally generated CANCELs"
201
+		" (see RFC3326)" },
202
+	{"e2e_cancel_reason",	CFG_VAR_INT | CFG_ATOMIC,	0, 1, 0, 0,
203
+		"if set to 1, Reason headers from received CANCELs are copied into"
204
+		" the corresponding generated hop-by-hop CANCELs"},
199 205
 	{0, 0, 0, 0, 0, 0}
200 206
 };
... ...
@@ -21,10 +21,9 @@
21 21
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22 22
  */
23 23
 
24
-/*!
25
- * \file 
26
- * \brief TM :: Configuration
27
- * \ingroup tm
24
+/** TM :: Runtime configuration variables.
25
+ * @file
26
+ * @ingroup tm
28 27
  */
29 28
 
30 29
 
... ...
@@ -136,6 +135,8 @@ struct cfg_group_tm {
136 136
 	int	reparse_on_dns_failover;
137 137
 	int disable_6xx;
138 138
 	int local_ack_mode;
139
+	int local_cancel_reason;
140
+	int e2e_cancel_reason;
139 141
 };
140 142
 
141 143
 extern struct cfg_group_tm	default_tm_cfg;
... ...
@@ -1213,7 +1213,9 @@ if (!t_next_contacts()) {
1213 1213
 				INVITE transactions) it will return true if the corresponding
1214 1214
 				INVITE transaction is found and still active and false if not.
1215 1215
 				</para>
1216
-				<note>Note that the e2e ACK matching is more of a hint
1216
+				<note>
1217
+				<para>
1218
+				Note that the e2e ACK matching is more of a hint
1217 1219
 				then a certainty. A delayed e2e ACK might arrive after the
1218 1220
 				transaction wait time elapses, when the INVITE transaction no
1219 1221
 				longer exists and thus would not match anything. There are
... ...
@@ -1221,6 +1223,7 @@ if (!t_next_contacts()) {
1221 1221
 				for e2e ACK matching (since this is not needed for a statefull
1222 1222
 				proxy and it requires additional memory, tm will not keep this
1223 1223
 				information unless needed by some other module or callbacks).
1224
+				</para>
1224 1225
 				</note>
1225 1226
 			</listitem>
1226 1227
 			<listitem>
... ...
@@ -1321,27 +1324,27 @@ route {
1321 1321
 	<para>
1322 1322
 		There are several function prototypes:
1323 1323
 		<itemizedlist>
1324
-		<listitem>
1324
+		<listitem><para>
1325 1325
 	    <function>t_replicate(uri)</function>,
1326
-		</listitem>
1327
-		<listitem>
1326
+		</para></listitem>
1327
+		<listitem><para>
1328 1328
 	    <function>t_replicate(host, port)</function>,
1329
-		</listitem>
1330
-		<listitem>
1329
+		</para></listitem>
1330
+		<listitem><para>
1331 1331
 	    <function>t_replicat_udp(host, port)</function>
1332
-		</listitem>
1333
-		<listitem>
1332
+		</para></listitem>
1333
+		<listitem><para>
1334 1334
 	    <function>t_replicate_tcp(host, port)</function>
1335
-		</listitem>
1336
-		<listitem>
1335
+		</para></listitem>
1336
+		<listitem><para>
1337 1337
 	    <function>t_replicate_tls(host, port)</function>
1338
-		</listitem>
1339
-		<listitem>
1338
+		</para></listitem>
1339
+		<listitem><para>
1340 1340
 	    <function>t_replicate_sctp(host, port)</function>
1341
-		</listitem>
1342
-		<listitem>
1341
+		</para></listitem>
1342
+		<listitem><para>
1343 1343
 	    <function>t_replicate_to(proto, hostport)</function>
1344
-		</listitem>
1344
+		</para></listitem>
1345 1345
 		</itemizedlist>
1346 1346
 	</para>
1347 1347
 	<para>Meaning of the parameters is as follows:</para>
... ...
@@ -1397,18 +1400,18 @@ t_replicate_to_udp("1.2.3.4", "5060");
1397 1397
 	<para>
1398 1398
 		There are several function prototypes:
1399 1399
 		<itemizedlist>
1400
-		<listitem>
1400
+		<listitem><para>
1401 1401
 	    <function>t_relay_to()</function>,
1402
-		</listitem>
1403
-		<listitem>
1402
+		</para></listitem>
1403
+		<listitem><para>
1404 1404
 	    <function>t_relay_to(proxy)</function>,
1405
-		</listitem>
1406
-		<listitem>
1405
+		</para></listitem>
1406
+		<listitem><para>
1407 1407
 	    <function>t_relay_to(flags)</function>
1408
-		</listitem>
1409
-		<listitem>
1408
+		</para></listitem>
1409
+		<listitem><para>
1410 1410
 	    <function>t_relay_to(proxy, flags)</function>
1411
-		</listitem>
1411
+		</para></listitem>
1412 1412
 		</itemizedlist>
1413 1413
 	</para>
1414 1414
 	<para>Meaning of the parameters is as follows:</para>
... ...
@@ -1457,4 +1460,36 @@ t_relay_to("0x01");
1457 1457
 	</example>
1458 1458
     </section>
1459 1459
 
1460
+
1461
+	<section id="t_set_no_e2e_cancel_reason">
1462
+	<title>
1463
+		<function>t_set_no_e2e_cancel_reason(0|1)</function>
1464
+	</title>
1465
+	<para>
1466
+		Enables/disables reason header (RFC 3326) copying from the triggering
1467
+		received CANCEL to the generated hop-by-hop CANCEL. 0 enables and
1468
+		1 disables.
1469
+	</para>
1470
+	<para>
1471
+		It overrides the <varname>e2e_cancel_reason</varname> setting (module
1472
+		 parameter) for the current transaction.
1473
+	</para>
1474
+	<para>
1475
+		See also: <varname>e2e_cancel_reason</varname>.
1476
+	</para>
1477
+	<example>
1478
+		<title><function>t_set_no_e2e_cancel_reason</function> usage</title>
1479
+		<programlisting>
1480
+...
1481
+route {
1482
+...
1483
+	if (src_ip!=10.0.0.0/8) #  don't trust CANCELs from the outside
1484
+		t_set_no_e2e_cancel_reason(1); # turn off CANCEL reason header copying
1485
+...
1486
+}
1487
+		</programlisting>
1488
+	</example>
1489
+	</section>
1490
+
1491
+
1460 1492
 </section>
... ...
@@ -45,9 +45,11 @@ modparam("tm", "fr_timer", 10000)
45 45
 	</para>
46 46
 	<para>
47 47
 	</para>
48
+	<para>
48 49
 		Note: this timer can be restarted when a provisional response is
49 50
 		received. For more details see
50 51
 		<varname>restart_fr_on_each_reply</varname>.
52
+	</para>
51 53
 	<para>
52 54
 	    Default value is 120000 ms (120 seconds).
53 55
 	</para>
... ...
@@ -1094,11 +1096,11 @@ modparam("tm", "disable_6xx_block", 1)
1094 1094
 		reply.
1095 1095
 		</para></listitem>
1096 1096
 	</itemizedlist>
1097
-	<note>
1097
+	<note><para>
1098 1098
 	Mode 1 and 2 break the rfc, but are useful to deal with some simple UAs
1099 1099
 	behind the NAT cases (no different routing for the ACK and the contact 
1100 1100
 	contains an address behind the NAT).
1101
-	</note>
1101
+	</para></note>
1102 1102
 	<para>
1103 1103
 		The default value is 0 (rfc conformant behaviour).
1104 1104
 	</para>
... ...
@@ -1172,4 +1174,66 @@ modparam("tm", "failure_reply_mode", 3)
1172 1172
 	</example>
1173 1173
 	</section>
1174 1174
 
1175
+
1176
+	<section id="local_cancel_reason">
1177
+		<title><varname>local_cancel_reason</varname> (boolean)</title>
1178
+		<para>
1179
+			Enables/disables adding reason headers (RFC 3326) for CANCELs
1180
+			generated due to receiving a final reply. The reason header added
1181
+			will look like: "Reason: SIP;cause=&lt;final_reply_code&gt;".
1182
+		</para>
1183
+		<para>
1184
+			Default value is 1 (enabled).
1185
+		</para>
1186
+		<para>
1187
+			Can be set at runtime, e.g.:
1188
+			<programlisting>
1189
+	$ sercmd cfg.set_now_int tm local_cancel_reason 0
1190
+			</programlisting>
1191
+		</para>
1192
+		<para>
1193
+			See also: <varname>e2e_cancel_reason</varname>.
1194
+		</para>
1195
+		<example>
1196
+			<title>Set <varname>local_cancel_reason</varname> parameter</title>
1197
+			<programlisting>
1198
+...
1199
+modparam("tm", "local_cancel_reason", "0")
1200
+...
1201
+			</programlisting>
1202
+		</example>
1203
+	</section>
1204
+
1205
+
1206
+	<section id="e2e_cancel_reason">
1207
+		<title><varname>e2e_cancel_reason</varname> (boolean)</title>
1208
+		<para>
1209
+			Enables/disables adding reason headers (RFC 3326) for CANCELs
1210
+			generated due to a received CANCEL.  If enabled the reason headers
1211
+			from received CANCELs will be copied into the generated hop-by-hop
1212
+			CANCELs.
1213
+		</para>
1214
+		<para>
1215
+			Default value is 1 (enabled).
1216
+		</para>
1217
+		<para>
1218
+			Can be changed at runtime, e.g.:
1219
+			<programlisting>
1220
+	$ sercmd cfg.set_now_int tm e2e_cancel_reason 0
1221
+			</programlisting>
1222
+		</para>
1223
+		<para>
1224
+			See also: <function>t_set_no_e2e_cancel_reason()</function> and
1225
+						<varname>local_cancel_reason</varname>.
1226
+		</para>
1227
+		<example>
1228
+			<title>Set <varname>e2e_cancel_reason</varname> parameter</title>
1229
+			<programlisting>
1230
+...
1231
+modparam("tm", "e2e_cancel_reason", "0")
1232
+...
1233
+			</programlisting>
1234
+		</example>
1235
+	</section>
1236
+
1175 1237
 </section>
... ...
@@ -1,6 +1,13 @@
1 1
 <?xml version="1.0" encoding="UTF-8"?>
2 2
 <!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" 
3
-   "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
3
+	"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
4
+	[ <!ENTITY % local.common.attrib
5
+	 "xmlns:xi CDATA #FIXED 'http://www.w3.org/2001/XInclude'">
6
+	 <!-- Include general documentation entities -->
7
+	 <!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
8
+	 %docentities;
9
+	]
10
+>
4 11
 
5 12
 <section id="tm" xmlns:xi="http://www.w3.org/2001/XInclude">
6 13
     <sectioninfo>
... ...
@@ -141,6 +141,10 @@ void free_cell( struct cell* dead_cell )
141 141
 		sip_msg_free_unsafe( dead_cell->uas.request );
142 142
 	if ( dead_cell->uas.response.buffer )
143 143
 		shm_free_unsafe( dead_cell->uas.response.buffer );
144
+#ifdef CANCEL_REASON_SUPPORT
145
+	if (unlikely(dead_cell->uas.cancel_reas))
146
+		shm_free_unsafe(dead_cell->uas.cancel_reas);
147
+#endif /* CANCEL_REASON_SUPPORT */
144 148
 
145 149
 	/* callbacks */
146 150
 	for( cbs=(struct tm_callback*)dead_cell->tmcb_hl.first ; cbs ; ) {
... ...
@@ -40,10 +40,9 @@
40 40
  *              inlined often used functions (andrei)
41 41
  */
42 42
 
43
-/*!
44
- * \file 
45
- * \brief TM :: 
46
- * \ingroup tm
43
+/**  TM :: hash table, flags and other general defines.
44
+ * @file 
45
+ * @ingroup tm
47 46
  */
48 47
 
49 48
 
... ...
@@ -192,6 +191,10 @@ typedef struct ua_server
192 192
 	 * we need them for dialog-wise matching of ACKs;
193 193
 	 * the pointer shows to shmem-ed reply */
194 194
 	str				 local_totag;
195
+#ifdef CANCEL_REASON_SUPPORT
196
+	struct cancel_reason* cancel_reas; /* pointer to cancel reason, used
197
+										  for e2e cancels */
198
+#endif /* CANCEL_REASON_SUPPORT */
195 199
 	unsigned int     status;
196 200
 }ua_server_type;
197 201
 
... ...
@@ -281,6 +284,9 @@ struct totag_elem {
281 281
 
282 282
 #define T_DISABLE_6xx (1<<8) /* treat 6xx as a normal reply */
283 283
 #define T_DISABLE_FAILOVER (1<<9) /* don't perform dns failover */
284
+#ifdef CANCEL_REASON_SUPPORT
285
+#define T_NO_E2E_CANCEL_REASON (1<<10) /* don't propagate CANCEL Reason */
286
+#endif /* CANCEL_REASON_SUPPORT */
284 287
 #define T_DONT_FORK   (T_CANCELED|T_6xx)
285 288
 
286 289
 /* unsigned short should be enough for a retr. timer: max. 65535 ticks =>
... ...
@@ -46,6 +46,7 @@
46 46
  *              reflecting its purpose
47 47
  *             prepare_to_cancel() takes now an additional skip_branches
48 48
  *              bitmap parameter (andrei)
49
+ * 2010-02-26  cancel reason (rfc3326) basic support (andrei)
49 50
  */
50 51
 
51 52
 #include <stdio.h> /* for FILE* in fifo_uac_cancel */
... ...
@@ -102,13 +103,14 @@ void prepare_to_cancel(struct cell *t, branch_bm_t *cancel_bm,
102 102
 
103 103
 /* cancel branches scheduled for deletion
104 104
  * params: t          - transaction
105
- *          cancel_bm - bitmap with the branches that are supposed to be 
106
- *                       canceled 
105
+ *          cancel_data - structure filled with the cancel bitmap (bitmap with
106
+ *                       the branches that are supposed to be canceled) and
107
+ *                       the cancel reason.
107 108
  *          flags     - how_to_cancel flags, see cancel_branch()
108 109
  * returns: bitmap with the still active branches (on fr timer)
109
- * WARNING: always fill cancel_bm using prepare_to_cancel(), supplying values
110
- *          in any other way is a bug*/
111
-int cancel_uacs( struct cell *t, branch_bm_t cancel_bm, int flags)
110
+ * WARNING: always fill cancel_data->cancel_bitmap using prepare_to_cancel(),
111
+ *          supplying values in any other way is a bug*/
112
+int cancel_uacs( struct cell *t, struct cancel_info* cancel_data, int flags)
112 113
 {
113 114
 	int i;
114 115
 	int ret;
... ...
@@ -117,10 +119,13 @@ int cancel_uacs( struct cell *t, branch_bm_t cancel_bm, int flags)
117 117
 	ret=0;
118 118
 	/* cancel pending client transactions, if any */
119 119
 	for( i=0 ; i<t->nr_of_outgoings ; i++ ) 
120
-		if (cancel_bm & (1<<i)){
120
+		if (cancel_data->cancel_bitmap & (1<<i)){
121 121
 			r=cancel_branch(
122 122
 				t,
123 123
 				i,
124
+#ifdef CANCEL_REASON_SUPPORT
125
+				&cancel_data->reason,
126
+#endif /* CANCEL_REASON_SUPPORT */
124 127
 				flags | ((t->uac[i].request.buffer==NULL)?
125 128
 					F_CANCEL_B_FAKE_REPLY:0) /* blind UAC? */
126 129
 			);
... ...
@@ -131,7 +136,7 @@ int cancel_uacs( struct cell *t, branch_bm_t cancel_bm, int flags)
131 131
 
132 132
 int cancel_all_uacs(struct cell *trans, int how)
133 133
 {
134
-	branch_bm_t cancel_bm;
134
+	struct cancel_info cancel_data;
135 135
 	int i,j;
136 136
 
137 137
 #ifdef EXTRA_DEBUG
... ...
@@ -139,10 +144,10 @@ int cancel_all_uacs(struct cell *trans, int how)
139 139
 #endif
140 140
 	DBG("Canceling T@%p [%u:%u]\n", trans, trans->hash_index, trans->label);
141 141
 	
142
-	cancel_bm=0;
143
-	prepare_to_cancel(trans, &cancel_bm, 0);
142
+	init_cancel_info(&cancel_data);
143
+	prepare_to_cancel(trans, &cancel_data.cancel_bitmap, 0);
144 144
 	 /* tell tm to cancel the call */
145
-	i=cancel_uacs(trans, cancel_bm, how);
145
+	i=cancel_uacs(trans, &cancel_data, how);
146 146
 	
147 147
 	if (how & F_CANCEL_UNREF)
148 148
 #ifndef TM_DEL_UNREF
... ...
@@ -172,6 +177,7 @@ int cancel_all_uacs(struct cell *trans, int how)
172 172
  *
173 173
  * params:  t - transaction
174 174
  *          branch - branch number to be canceled
175
+ *          reason - cancel reason structure
175 176
  *          flags - howto cancel: 
176 177
  *                   F_CANCEL_B_KILL - will completely stop the 
177 178
  *                     branch (stops the timers), use with care
... ...
@@ -202,13 +208,17 @@ int cancel_all_uacs(struct cell *trans, int how)
202 202
  *          - checking for buffer==0 under REPLY_LOCK is no enough, an 
203 203
  *           atomic_cmpxhcg or atomic_get_and_set _must_ be used.
204 204
  */
205
-int cancel_branch( struct cell *t, int branch, int flags )
205
+int cancel_branch( struct cell *t, int branch,
206
+	#ifdef CANCEL_REASON_SUPPORT
207
+					struct cancel_reason* reason,
208
+	#endif /* CANCEL_REASON_SUPPORT */
209
+					int flags )
206 210
 {
207 211
 	char *cancel;
208 212
 	unsigned int len;
209 213
 	struct retr_buf *crb, *irb;
210 214
 	int ret;
211
-	branch_bm_t tmp_bm;
215
+	struct cancel_info tmp_cd;
212 216
 	void* pcbuf;
213 217
 
214 218
 	crb=&t->uac[branch].local_cancel;
... ...
@@ -236,7 +246,7 @@ int cancel_branch( struct cell *t, int branch, int flags )
236 236
 			atomic_set_long(pcbuf, 0);
237 237
 			if (flags & F_CANCEL_B_FAKE_REPLY){
238 238
 				LOCK_REPLIES(t);
239
-				if (relay_reply(t, FAKED_REPLY, branch, 487, &tmp_bm, 1) == 
239
+				if (relay_reply(t, FAKED_REPLY, branch, 487, &tmp_cd, 1) == 
240 240
 										RPS_ERROR){
241 241
 					return -1;
242 242
 				}
... ...
@@ -257,7 +267,7 @@ int cancel_branch( struct cell *t, int branch, int flags )
257 257
 				if (flags & F_CANCEL_B_FAKE_REPLY){
258 258
 					stop_rb_timers( irb ); /* stop even the fr timer */
259 259
 					LOCK_REPLIES(t);
260
-					if (relay_reply(t, FAKED_REPLY, branch, 487, &tmp_bm, 1)== 
260
+					if (relay_reply(t, FAKED_REPLY, branch, 487, &tmp_cd, 1)== 
261 261
 											RPS_ERROR){
262 262
 						return -1;
263 263
 					}
... ...
@@ -272,10 +282,19 @@ int cancel_branch( struct cell *t, int branch, int flags )
272 272
 
273 273
 	if (cfg_get(tm, tm_cfg, reparse_invite)) {
274 274
 		/* build the CANCEL from the INVITE which was sent out */
275
-		cancel = build_local_reparse(t, branch, &len, CANCEL, CANCEL_LEN, &t->to);
275
+		cancel = build_local_reparse(t, branch, &len, CANCEL, CANCEL_LEN,
276
+									 &t->to
277
+	#ifdef CANCEL_REASON_SUPPORT
278
+									 , reason
279
+	#endif /* CANCEL_REASON_SUPPORT */
280
+									 );
276 281
 	} else {
277
-		/* build the CANCEL from the reveived INVITE */
278
-		cancel = build_local(t, branch, &len, CANCEL, CANCEL_LEN, &t->to);
282
+		/* build the CANCEL from the received INVITE */
283
+		cancel = build_local(t, branch, &len, CANCEL, CANCEL_LEN, &t->to
284
+	#ifdef CANCEL_REASON_SUPPORT
285
+								, reason
286
+	#endif /* CANCEL_REASON_SUPPORT */
287
+								);
279 288
 	}
280 289
 	if (!cancel) {
281 290
 		LOG(L_ERR, "ERROR: attempt to build a CANCEL failed\n");
... ...
@@ -333,7 +352,7 @@ void rpc_cancel(rpc_t* rpc, void* c)
333 333
 {
334 334
 	struct cell *trans;
335 335
 	static char cseq[128], callid[128];
336
-	branch_bm_t cancel_bm;
336
+	struct cancel_info cancel_data;
337 337
 	int i,j;
338 338
 
339 339
 	str cseq_s;   /* cseq */
... ...
@@ -341,7 +360,7 @@ void rpc_cancel(rpc_t* rpc, void* c)
341 341
 
342 342
 	cseq_s.s=cseq;
343 343
 	callid_s.s=callid;
344
-	cancel_bm=0;
344
+	init_cancel_info(&cancel_data);
345 345
 
346 346
 	if (rpc->scan(c, "SS", &callid_s, &cseq_s) < 2) {
347 347
 		rpc->fault(c, 400, "Callid and CSeq expected as parameters");
... ...
@@ -354,10 +373,10 @@ void rpc_cancel(rpc_t* rpc, void* c)
354 354
 		return;
355 355
 	}
356 356
 	/*  find the branches that need cancel-ing */
357
-	prepare_to_cancel(trans, &cancel_bm, 0);
357
+	prepare_to_cancel(trans, &cancel_data.cancel_bitmap, 0);
358 358
 	 /* tell tm to cancel the call */
359 359
 	DBG("Now calling cancel_uacs\n");
360
-	i=cancel_uacs(trans, cancel_bm, 0); /* don't fake 487s, 
360
+	i=cancel_uacs(trans, &cancel_data, 0); /* don't fake 487s, 
361 361
 										 just wait for timeout */
362 362
 	
363 363
 	/* t_lookup_callid REF`d the transaction for us, we must UNREF here! */
... ...
@@ -35,6 +35,7 @@
35 35
  *  2009-07-14  should_cancel_branch() renamed to prepare_cancel_branch() to
36 36
  *               better reflect its purpose
37 37
  *              which_cancel() renamed to prepare_to_cancel() (andrei)
38
+ * 2010-02-26  cancel reason (rfc3326) basic support (andrei)
38 39
  */
39 40
 
40 41
 
... ...
@@ -46,6 +47,7 @@
46 46
 #include "../../atomic_ops.h"
47 47
 #include "defs.h"
48 48
 #include "h_table.h"
49
+#include "t_reply.h"
49 50
 
50 51
 
51 52
 /* a buffer is empty but cannot be used by anyone else;
... ...
@@ -79,9 +81,13 @@
79 79
 
80 80
 
81 81
 void prepare_to_cancel(struct cell *t, branch_bm_t *cancel_bm, branch_bm_t s);
82
-int cancel_uacs( struct cell *t, branch_bm_t cancel_bm, int flags );
82
+int cancel_uacs( struct cell *t, struct cancel_info* cancel_data, int flags );
83 83
 int cancel_all_uacs(struct cell *trans, int how);
84
-int cancel_branch( struct cell *t, int branch, int flags );
84
+int cancel_branch( struct cell *t, int branch,
85
+#ifdef CANCEL_REASON_SUPPORT
86
+					struct cancel_reason* reason,
87
+#endif /* CANCEL_REASON_SUPPORT */
88
+					int flags );
85 89
 
86 90
 typedef int(*cancel_uacs_f)( struct cell *t, branch_bm_t cancel_bm,
87 91
 								int flags );
... ...
@@ -929,7 +929,11 @@ int e2e_cancel_branch( struct sip_msg *cancel_msg, struct cell *t_cancel,
929 929
 			"thus lumps are not applied to the message!\n");
930 930
 		}
931 931
 		shbuf=build_local_reparse( t_invite, branch, &len, CANCEL,
932
-									CANCEL_LEN, &t_invite->to);
932
+									CANCEL_LEN, &t_invite->to
933
+#ifdef CANCEL_REASON_SUPPORT
934
+									, 0
935
+#endif /* CANCEL_REASON_SUPPORT */
936
+									);
933 937
 		if (unlikely(!shbuf)) {
934 938
 			LOG(L_ERR, "e2e_cancel_branch: printing e2e cancel failed\n");
935 939
 			ret=ser_error=E_OUT_OF_MEM;
... ...
@@ -962,13 +966,118 @@ error:
962 962
 
963 963
 
964 964
 
965
-void e2e_cancel( struct sip_msg *cancel_msg, 
965
+#ifdef CANCEL_REASON_SUPPORT
966
+/** create a cancel reason structure packed into a single shm. block.
967
+  * From a cause and a pointer to a str or cancel_msg, build a
968
+  * packed cancel reason structure (CANCEL_REAS_PACKED_HDRS), using a
969
+  * single memory allocation (so that it can be freed by a simple shm_free().
970
+  * @param cause - cancel cause, @see cancel_reason for more details.
971
+  * @param data - depends on the cancel cause.
972
+  * @return pointer to shm. packed cancel reason struct. on success,
973
+  *        0 on error
974
+  */
975
+static struct cancel_reason* cancel_reason_pack(short cause, void* data,
976
+													struct cell* t)
977
+{
978
+	char* d;
979
+	struct cancel_reason* cr;
980
+	int reason_len;
981
+	int code_len;
982
+	struct hdr_field *reas1, *reas_last, *hdr;
983
+	str* txt;
984
+	struct sip_msg* e2e_cancel;
985
+	
986
+	if (likely(cause != CANCEL_REAS_UNKNOWN)){
987
+		reason_len = 0;
988
+		txt = 0;
989
+		e2e_cancel = 0;
990
+		reas1 = 0;
991
+		reas_last = 0;
992
+		if (likely(cause == CANCEL_REAS_RCVD_CANCEL &&
993
+					data && !(t->flags & T_NO_E2E_CANCEL_REASON))) {
994
+			/* parse the entire cancel, to get all the Reason headers */
995
+			e2e_cancel = data;
996
+			parse_headers(e2e_cancel, HDR_EOH_F, 0);
997
+			for(hdr=get_hdr(e2e_cancel, HDR_REASON_T), reas1=hdr;
998
+					hdr; hdr=next_sibling_hdr(hdr)) {
999
+				/* hdr->len includes CRLF */
1000
+				reason_len += hdr->len;
1001
+				reas_last=hdr;
1002
+			}
1003
+		} else if (likely(cause > 0 &&
1004
+					cfg_get(tm, tm_cfg, local_cancel_reason))){
1005
+			txt = (str*) data;
1006
+			/* Reason: SIP;cause=<reason->cause>[;text=<reason->u.text.s>] */
1007
+			reason_len = REASON_PREFIX_LEN + USHORT2SBUF_MAX_LEN +
1008
+				((txt && txt->s)?
1009
+					REASON_TEXT_LEN + 1 + txt->len + 1 : 0) +
1010
+				CRLF_LEN;
1011
+		} else if (cause == CANCEL_REAS_PACKED_HDRS &&
1012
+					!(t->flags & T_NO_E2E_CANCEL_REASON) && data) {
1013
+			txt = (str*) data;
1014
+			reason_len = txt?txt->len:0;
1015
+		} else if (unlikely(cause < CANCEL_REAS_MIN)) {
1016
+			BUG("unhandled reason cause %d\n", cause);
1017
+			goto error;
1018
+		}
1019
+		
1020
+		if (unlikely(reason_len == 0))
1021
+			return 0; /* nothing to do, no reason */
1022
+		cr = shm_malloc(sizeof(struct cancel_reason) + reason_len);
1023
+		if (unlikely(cr == 0))
1024
+				goto error;
1025
+		d = (char*)cr +sizeof(*cr);
1026
+		cr->cause = CANCEL_REAS_PACKED_HDRS;
1027
+		cr->u.packed_hdrs.s = d;
1028
+		cr->u.packed_hdrs.len = reason_len;
1029
+		
1030
+		if (cause == CANCEL_REAS_RCVD_CANCEL) {
1031
+			for(hdr=reas1; hdr; hdr=next_sibling_hdr(hdr)) {
1032
+				/* hdr->len includes CRLF */
1033
+				append_str(d, hdr->name.s, hdr->len);
1034
+				if (likely(hdr==reas_last))
1035
+					break;
1036
+			}
1037
+		} else if (likely(cause > 0)) {
1038
+			append_str(d, REASON_PREFIX, REASON_PREFIX_LEN);
1039
+			code_len=ushort2sbuf(cause, d, reason_len - 
1040
+									(int)(d - (char*)cr - sizeof(*cr)));
1041
+			if (unlikely(code_len==0)) {
1042
+				shm_free(cr);
1043
+				cr = 0;
1044
+				BUG("not enough space to write reason code");
1045
+				goto error;
1046
+			}
1047
+			d+=code_len;
1048
+			if (txt && txt->s){
1049
+				append_str(d, REASON_TEXT, REASON_TEXT_LEN);
1050
+				*d='"'; d++;
1051
+				append_str(d, txt->s, txt->len);
1052
+				*d='"'; d++;
1053
+			}
1054
+			append_str(d, CRLF, CRLF_LEN);
1055
+		} else if (cause == CANCEL_REAS_PACKED_HDRS) {
1056
+			append_str(d, txt->s, txt->len);
1057
+		}
1058
+		return cr;
1059
+	}
1060
+error:
1061
+	return 0;
1062
+}
1063
+#endif /* CANCEL_REASON_SUPPORT */
1064
+
1065
+
1066
+
1067
+void e2e_cancel( struct sip_msg *cancel_msg,
966 1068
 	struct cell *t_cancel, struct cell *t_invite )
967 1069
 {
968 1070
 	branch_bm_t cancel_bm;
969 1071
 #ifndef E2E_CANCEL_HOP_BY_HOP
970 1072
 	branch_bm_t tmp_bm;
971
-#endif
1073
+#elif defined (CANCEL_REASON_SUPPORT)
1074
+	struct cancel_reason* reason;
1075
+	int free_reason;
1076
+#endif /* E2E_CANCEL_HOP_BY_HOP */
972 1077
 	int i;
973 1078
 	int lowest_error;
974 1079
 	int ret;
... ...
@@ -1013,6 +1122,21 @@ void e2e_cancel( struct sip_msg *cancel_msg,
1013 1013
 	 * have 0 branches and we check for the branch number in 
1014 1014
 	 * t_reply_matching() ).
1015 1015
 	 */
1016
+#ifdef CANCEL_REASON_SUPPORT
1017
+	free_reason = 0;
1018
+	reason = 0;
1019
+	if (likely(t_invite->uas.cancel_reas == 0)){
1020
+		reason = cancel_reason_pack(CANCEL_REAS_RCVD_CANCEL, cancel_msg,
1021
+									t_invite);
1022
+		/* set if not already set */
1023
+		if (unlikely(reason &&
1024
+					atomic_cmpxchg_long((void*)&t_invite->uas.cancel_reas,
1025
+										0, (long)reason) != 0)) {
1026
+			/* already set, failed to re-set it */
1027
+			free_reason = 1;
1028
+		}
1029
+	}
1030
+#endif /* CANCEL_REASON_SUPPORT */
1016 1031
 	for (i=0; i<t_invite->nr_of_outgoings; i++)
1017 1032
 		if (cancel_bm & (1<<i)) {
1018 1033
 			/* it's safe to get the reply lock since e2e_cancel is
... ...
@@ -1022,6 +1146,9 @@ void e2e_cancel( struct sip_msg *cancel_msg,
1022 1022
 			ret=cancel_branch(
1023 1023
 				t_invite,
1024 1024
 				i,
1025
+#ifdef CANCEL_REASON_SUPPORT
1026
+				reason,
1027
+#endif /* CANCEL_REASON_SUPPORT */
1025 1028
 				cfg_get(tm,tm_cfg, cancel_b_flags)
1026 1029
 					| ((t_invite->uac[i].request.buffer==NULL)?
1027 1030
 						F_CANCEL_B_FAKE_REPLY:0) /* blind UAC? */
... ...
@@ -1029,6 +1156,12 @@ void e2e_cancel( struct sip_msg *cancel_msg,
1029 1029
 			if (ret<0) cancel_bm &= ~(1<<i);
1030 1030
 			if (ret<lowest_error) lowest_error=ret;
1031 1031
 		}
1032
+#ifdef CANCEL_REASON_SUPPORT
1033
+	if (unlikely(free_reason)) {
1034
+		/* reason was not set as the global reason => free it */
1035
+		shm_free(reason);
1036
+	}
1037
+#endif /* CANCEL_REASON_SUPPORT */
1032 1038
 #else /* ! E2E_CANCEL_HOP_BY_HOP */
1033 1039
 	/* fix label -- it must be same for reply matching (the label is part of
1034 1040
 	 * the generated via branch for the cancels sent upstream and if it
... ...
@@ -1266,6 +1266,10 @@ static inline void init_new_t(struct cell *new_cell, struct sip_msg *p_msg)
1266 1266
 					(!cfg_get(tm, tm_cfg, tm_auto_inv_100) -1);
1267 1267
 		new_cell->flags|=T_DISABLE_6xx &
1268 1268
 					(!cfg_get(tm, tm_cfg, disable_6xx) -1);
1269
+#ifdef CANCEL_REASON_SUPPORT
1270
+		new_cell->flags|=T_NO_E2E_CANCEL_REASON &
1271
+					(!!cfg_get(tm, tm_cfg, e2e_cancel_reason) -1);
1272
+#endif /* CANCEL_REASON_SUPPORT */
1269 1273
 		/* reset flags */
1270 1274
 		new_cell->flags &=
1271 1275
 			(~ get_msgid_val(user_cell_reset_flags, p_msg->id, int));
... ...
@@ -46,6 +46,7 @@
46 46
  *               resolving nexthop twice (andrei)
47 47
  * 2007-05-28: build_local_reparse() is introdued: it uses the outgoing
48 48
  *             INVITE as a source to construct a CANCEL or ACK (Miklos)
49
+ * 2010-02-26  cancel reason (rfc3326) basic support (andrei)
49 50
  */
50 51
 
51 52
 #include "defs.h"
... ...
@@ -72,6 +73,7 @@
72 72
 #include "../../cfg_core.h" /* cfg_get(core, core_cfg, use_dns_failover) */
73 73
 #endif
74 74
 
75
+
75 76
 /* convenience macros */
76 77
 #define memapp(_d,_s,_len) \
77 78
 	do{\
... ...
@@ -84,7 +86,11 @@
84 84
    customers of this function are local ACK and local CANCEL
85 85
  */
86 86
 char *build_local(struct cell *Trans,unsigned int branch,
87
-	unsigned int *len, char *method, int method_len, str *to)
87
+	unsigned int *len, char *method, int method_len, str *to
88
+#ifdef CANCEL_REASON_SUPPORT
89
+	, struct cancel_reason* reason
90
+#endif /* CANCEL_REASON_SUPPORT */
91
+	)
88 92
 {
89 93
 	char                *cancel_buf, *p, *via;
90 94
 	unsigned int         via_len;
... ...
@@ -94,6 +100,10 @@ char *build_local(struct cell *Trans,unsigned int branch,
94 94
 	str branch_str;
95 95
 	str via_id;
96 96
 	struct hostport hp;
97
+#ifdef CANCEL_REASON_SUPPORT
98
+	int reason_len, code_len;
99
+	struct hdr_field *reas1, *reas_last;
100
+#endif /* CANCEL_REASON_SUPPORT */
97 101
 
98 102
 	/* init */
99 103
 	via_id.s=0;
... ...
@@ -157,7 +167,40 @@ char *build_local(struct cell *Trans,unsigned int branch,
157 157
 		*len += user_agent_hdr.len + CRLF_LEN;
158 158
 	}
159 159
 	/* Content Length, EoM */
160
-	*len+=CONTENT_LENGTH_LEN+1 + CRLF_LEN + CRLF_LEN;
160
+	*len+=CONTENT_LENGTH_LEN+1 + CRLF_LEN;
161
+#ifdef CANCEL_REASON_SUPPORT
162
+	reason_len = 0;
163
+	reas1 = 0;
164
+	reas_last = 0;
165
+	/* compute reason size (if no reason or disabled => reason_len == 0)*/
166
+	if (reason && reason->cause != CANCEL_REAS_UNKNOWN){
167
+		if (likely(reason->cause > 0 &&
168
+					cfg_get(tm, tm_cfg, local_cancel_reason))){
169
+			/* Reason: SIP;cause=<reason->cause>[;text=<reason->u.text.s>] */
170
+			reason_len = REASON_PREFIX_LEN + USHORT2SBUF_MAX_LEN +
171
+				(reason->u.text.s?
172
+					REASON_TEXT_LEN + 1 + reason->u.text.len + 1 : 0) +
173
+				CRLF_LEN;
174
+		} else if (likely(reason->cause == CANCEL_REAS_PACKED_HDRS &&
175
+					!(Trans->flags & T_NO_E2E_CANCEL_REASON))) {
176
+			reason_len = reason->u.packed_hdrs.len;
177
+		} else if (reason->cause == CANCEL_REAS_RCVD_CANCEL &&
178
+					reason->u.e2e_cancel &&
179
+					!(Trans->flags & T_NO_E2E_CANCEL_REASON)) {
180
+			/* parse the entire cancel, to get all the Reason headers */
181
+			parse_headers(reason->u.e2e_cancel, HDR_EOH_F, 0);
182
+			for(hdr=get_hdr(reason->u.e2e_cancel, HDR_REASON_T), reas1=hdr;
183
+					hdr; hdr=next_sibling_hdr(hdr)) {
184
+				/* hdr->len includes CRLF */
185
+				reason_len += hdr->len;
186
+				reas_last=hdr;
187
+			}
188
+		} else if (unlikely(reason->cause < CANCEL_REAS_MIN))
189
+			BUG("unhandled reason cause %d\n", reason->cause);
190
+	}
191
+	*len+= reason_len;
192
+#endif /* CANCEL_REASON_SUPPORT */
193
+	*len+= CRLF_LEN; /* end of msg. */
161 194
 
162 195
 	cancel_buf=shm_malloc( *len+1 );
163 196
 	if (!cancel_buf)
... ...
@@ -197,9 +240,38 @@ char *build_local(struct cell *Trans,unsigned int branch,
197 197
 		append_str(p, user_agent_hdr.s, user_agent_hdr.len );
198 198
 		append_str(p, CRLF, CRLF_LEN );
199 199
 	}
200
-	/* Content Length, EoM */
201
-	append_str(p, CONTENT_LENGTH "0" CRLF CRLF ,
202
-		CONTENT_LENGTH_LEN+1 + CRLF_LEN + CRLF_LEN);
200
+	/* Content Length */
201
+	append_str(p, CONTENT_LENGTH "0" CRLF, CONTENT_LENGTH_LEN + 1 + CRLF_LEN);
202
+#ifdef CANCEL_REASON_SUPPORT
203
+	/* add reason if needed */
204
+	if (reason_len) {
205
+		if (likely(reason->cause > 0)) {
206
+			append_str(p, REASON_PREFIX, REASON_PREFIX_LEN);
207
+			code_len=ushort2sbuf(reason->cause, p,
208
+									*len-(int)(p-cancel_buf));
209
+			if (unlikely(code_len==0))
210
+				BUG("not enough space to write reason code");
211
+			p+=code_len;
212
+			if (reason->u.text.s){
213
+				append_str(p, REASON_TEXT, REASON_TEXT_LEN);
214
+				*p='"'; p++;
215
+				append_str(p, reason->u.text.s, reason->u.text.len);
216
+				*p='"'; p++;
217
+			}
218
+			append_str(p, CRLF, CRLF_LEN);
219
+		} else if (likely(reason->cause == CANCEL_REAS_PACKED_HDRS)) {
220
+			append_str(p, reason->u.packed_hdrs.s, reason->u.packed_hdrs.len);
221
+		} else if (reason->cause == CANCEL_REAS_RCVD_CANCEL) {
222
+			for(hdr=reas1; hdr; hdr=next_sibling_hdr(hdr)) {
223
+				/* hdr->len includes CRLF */
224
+				append_str(p, hdr->name.s, hdr->len);
225
+				if (likely(hdr==reas_last))
226
+					break;
227
+			}
228
+		}
229
+	}
230
+#endif /* CANCEL_REASON_SUPPORT */
231
+	append_str(p, CRLF, CRLF_LEN); /* msg. end */
203 232
 	*p=0;
204 233
 
205 234
 	pkg_free(via);
... ...
@@ -217,7 +289,11 @@ error:
217 217
  * Can not be used to build other type of requests!
218 218
  */
219 219
 char *build_local_reparse(struct cell *Trans,unsigned int branch,
220
-	unsigned int *len, char *method, int method_len, str *to)
220
+	unsigned int *len, char *method, int method_len, str *to
221
+#ifdef CANCEL_REASON_SUPPORT
222
+	, struct cancel_reason *reason
223
+#endif /* CANCEL_REASON_SUPPORT */
224
+	)
221 225
 {
222 226
 	char	*invite_buf, *invite_buf_end;
223 227
 	char	*cancel_buf;
... ...
@@ -225,6 +301,11 @@ char *build_local_reparse(struct cell *Trans,unsigned int branch,
225 225
 	short	invite_len;
226 226
 	enum _hdr_types_t	hf_type;
227 227
 	int	first_via, to_len;
228
+	int cancel_buf_len;
229
+#ifdef CANCEL_REASON_SUPPORT
230
+	int reason_len, code_len;
231
+	struct hdr_field *reas1, *reas_last, *hdr;
232
+#endif /* CANCEL_REASON_SUPPORT */
228 233
 
229 234
 	invite_buf = Trans->uac[branch].request.buffer;
230 235
 	invite_len = Trans->uac[branch].request.buffer_len;
... ...
@@ -234,9 +315,43 @@ char *build_local_reparse(struct cell *Trans,unsigned int branch,
234 234
 		goto error;
235 235
 	}
236 236
 	if ((*invite_buf != 'I') && (*invite_buf != 'i')) {
237
-		LOG(L_ERR, "ERROR: build_local_reparse: trying to call build_local_reparse() for a non-INVITE request?\n");
237
+		LOG(L_ERR, "ERROR: trying to call build_local_reparse()"
238
+					" for a non-INVITE request?\n");
238 239
 		goto error;
239 240
 	}
241
+	
242
+#ifdef CANCEL_REASON_SUPPORT
243
+	reason_len = 0;
244
+	reas1 = 0;
245
+	reas_last = 0;
246
+	/* compute reason size (if no reason or disabled => reason_len == 0)*/
247
+	if (reason && reason->cause != CANCEL_REAS_UNKNOWN){
248
+		if (likely(reason->cause > 0 &&
249
+					cfg_get(tm, tm_cfg, local_cancel_reason))){
250
+			/* Reason: SIP;cause=<reason->cause>[;text=<reason->u.text.s>] */
251
+			reason_len = REASON_PREFIX_LEN + USHORT2SBUF_MAX_LEN +
252
+				(reason->u.text.s?
253
+					REASON_TEXT_LEN + 1 + reason->u.text.len + 1 : 0) +
254
+				CRLF_LEN;
255
+		} else if (likely(reason->cause == CANCEL_REAS_PACKED_HDRS &&
256
+					!(Trans->flags & T_NO_E2E_CANCEL_REASON))) {
257
+			reason_len = reason->u.packed_hdrs.len;
258
+		} else if (reason->cause == CANCEL_REAS_RCVD_CANCEL &&
259
+					reason->u.e2e_cancel &&
260
+					!(Trans->flags & T_NO_E2E_CANCEL_REASON)) {
261
+			/* parse the entire cancel, to get all the Reason headers */
262
+			parse_headers(reason->u.e2e_cancel, HDR_EOH_F, 0);
263
+			for(hdr=get_hdr(reason->u.e2e_cancel, HDR_REASON_T), reas1=hdr;
264
+					hdr; hdr=next_sibling_hdr(hdr)) {
265
+				/* hdr->len includes CRLF */
266
+				reason_len += hdr->len;
267
+				reas_last=hdr;
268
+			}
269
+		} else if (unlikely(reason->cause < CANCEL_REAS_MIN))
270
+			BUG("unhandled reason cause %d\n", reason->cause);
271
+	}
272
+#endif /* CANCEL_REASON_SUPPORT */
273
+
240 274
 	invite_buf_end = invite_buf + invite_len;
241 275
 	s = invite_buf;
242 276
 
... ...
@@ -245,10 +360,15 @@ char *build_local_reparse(struct cell *Trans,unsigned int branch,
245 245
 	I just extend it with the length of new To HF to be sure.
246 246
 	Ugly, but we avoid lots of checks and memory allocations this way */
247 247
 	to_len = to ? to->len : 0;
248
-	cancel_buf = shm_malloc(sizeof(char)*(invite_len + to_len));
248
+#ifdef CANCEL_REASON_SUPPORT
249
+	cancel_buf_len = invite_len + to_len + reason_len;
250
+#else
251
+	cancel_buf_len = invite_len + to_len;
252
+#endif /* CANCEL_REASON_SUPPORT */
253
+	cancel_buf = shm_malloc(sizeof(char)*cancel_buf_len);
249 254
 	if (!cancel_buf)
250 255
 	{
251
-		LOG(L_ERR, "ERROR: build_local_reparse: cannot allocate shared memory\n");
256
+		LOG(L_ERR, "ERROR: cannot allocate shared memory\n");
252 257
 		goto error;
253 258
 	}
254 259
 	d = cancel_buf;
... ...
@@ -281,7 +401,8 @@ char *build_local_reparse(struct cell *Trans,unsigned int branch,
281 281
 			case HDR_CSEQ_T:
282 282
 				/* find the method name and replace it */
283 283
 				while ((s < invite_buf_end)
284
-					&& ((*s == ':') || (*s == ' ') || (*s == '\t') || ((*s >= '0') && (*s <= '9')))
284
+					&& ((*s == ':') || (*s == ' ') || (*s == '\t') ||
285
+						((*s >= '0') && (*s <= '9')))
285 286
 					) s++;
286 287
 				append_str(d, s1, s - s1);
287 288
 				append_str(d, method, method_len);
... ...
@@ -300,7 +421,8 @@ char *build_local_reparse(struct cell *Trans,unsigned int branch,
300 300
 
301 301
 			case HDR_TO_T:
302 302
 				if (to_len == 0) {
303
-					/* there is no To tag required, just copy paste the header */
303
+					/* there is no To tag required, just copy paste
304
+					   the header */
304 305
 					s = lw_next_line(s, invite_buf_end);
305 306
 					append_str(d, s1, s - s1);
306 307
 				} else {
... ...
@@ -336,6 +458,41 @@ char *build_local_reparse(struct cell *Trans,unsigned int branch,
336 336
 
337 337
 			case HDR_EOH_T:
338 338
 				/* end of SIP message found */
339
+#ifdef CANCEL_REASON_SUPPORT
340
+				/* add reason if needed */