1MSRP Module 2 3Daniel-Constantin Mierla 4 5 <miconda@gmail.com> 6 7Edited by 8 9Daniel-Constantin Mierla 10 11 <miconda@gmail.com> 12 13Alex Balashov 14 15 <abalashov@evaristesys.com> 16 17 Copyright © 2012 asipto.com 18 __________________________________________________________________ 19 20 Table of Contents 21 22 1. Admin Guide 23 24 1. Overview 25 2. Dependencies 26 27 2.1. Kamailio Modules 28 2.2. External Libraries or Applications 29 30 3. Parameters 31 32 3.1. sipmsg (int) 33 3.2. cmap_size (int) 34 3.3. timer_interval (int) 35 3.4. auth_min_expires (int) 36 3.5. auth_max_expires (int) 37 3.6. use_path_addr (str) 38 3.7. event_callback (str) 39 40 4. Functions 41 42 4.1. msrp_relay() 43 4.2. msrp_reply(code, text [, hdrs]) 44 4.3. msrp_is_request() 45 4.4. msrp_is_reply() 46 4.5. msrp_set_dst(addr, sock) 47 4.6. msrp_relay_flags(flags) 48 4.7. msrp_reply_flags(flags) 49 4.8. msrp_cmap_save() 50 4.9. msrp_cmap_lookup() 51 52 5. Pseudo Variables 53 6. RPC Commands 54 55 6.1. msrp.cmaplist 56 57 7. Event Routes 58 8. Usage 59 60 List of Examples 61 62 1.1. Set sipmsg parameter 63 1.2. Set cmap_size parameter 64 1.3. Set timer_interval parameter 65 1.4. Set auth_min_expires parameter 66 1.5. Set auth_max_expires parameter 67 1.6. Set use_path_addr parameter 68 1.7. Set event_callback parameter 69 1.8. msrp_relay usage 70 1.9. msrp_reply usage 71 1.10. msrp_is_request usage 72 1.11. msrp_is_reply usage 73 1.12. msrp_set_dst usage 74 1.13. msrp_relay_flags usage 75 1.14. msrp_reply_flags usage 76 1.15. msrp_cmap_save usage 77 1.16. msrp_cmap_lookup usage 78 1.17. Event Route (using htable for MSRP connection tracking) 79 1.18. Event Route (using msrp_cmap_ functions for connection tracking) 80 81Chapter 1. Admin Guide 82 83 Table of Contents 84 85 1. Overview 86 2. Dependencies 87 88 2.1. Kamailio Modules 89 2.2. External Libraries or Applications 90 91 3. Parameters 92 93 3.1. sipmsg (int) 94 3.2. cmap_size (int) 95 3.3. timer_interval (int) 96 3.4. auth_min_expires (int) 97 3.5. auth_max_expires (int) 98 3.6. use_path_addr (str) 99 3.7. event_callback (str) 100 101 4. Functions 102 103 4.1. msrp_relay() 104 4.2. msrp_reply(code, text [, hdrs]) 105 4.3. msrp_is_request() 106 4.4. msrp_is_reply() 107 4.5. msrp_set_dst(addr, sock) 108 4.6. msrp_relay_flags(flags) 109 4.7. msrp_reply_flags(flags) 110 4.8. msrp_cmap_save() 111 4.9. msrp_cmap_lookup() 112 113 5. Pseudo Variables 114 6. RPC Commands 115 116 6.1. msrp.cmaplist 117 118 7. Event Routes 119 8. Usage 120 1211. Overview 122 123 This module provides a MSRP routing engine, a.k.a. MSRP relay. MSRP 124 (Message Session Relay Protocol) is defined by RFC4975, and the 125 extensions for an MSRP relay are covered in RFC4976. 126 127 A typical use of MSRP is instant messaging sessions initiated via SIP. 128 Unlike page-mode instant messaging, which is done via the SIP MESSAGE 129 request, MSRP uses a different communication channel which is 130 negotiated via INVITE-200 OK-ACK. 131 132 However, MSRP is still a text-based protocol. It uses several routing 133 mechanisms similar to what exists in SIP. Furthermore, MSRP requires 134 TCP, and recommends TLS for confidentiality and security. In light of 135 the scalability and performance of Kamailio in handling TCP/TLS, this 136 module reuses Kamailio's core framework to offer MSRP routing 137 capabilities. Along with embedded Presence and XCAP servers, Kamailio 138 now offers a complete solution for SIP beyond VoIP. 139 140 One of the main benefits of this module is the ability to reuse all the 141 other extensions that exist in the SIP server, including accounting, 142 authentication, authorization to database connectors, security and DoS 143 attack protections. 144 145 Kamailio can handle SIP and MSRP traffic received on the same port; the 146 appropriate configuration file block being executed based on the type 147 of message. Therefore, you can use Kamailio as a stand-alone MSRP relay 148 or you can have an instance handling both SIP and MSRP. Another option 149 is to configure Kamailio to listen on multiple ports, some of them for 150 SIP and others for MSRP. 151 1522. Dependencies 153 154 2.1. Kamailio Modules 155 2.2. External Libraries or Applications 156 1572.1. Kamailio Modules 158 159 The following modules must be loaded before this module: 160 * None. 161 162 The following modules are required to make proper use of this module: 163 * tls. 164 1652.2. External Libraries or Applications 166 167 The following libraries or applications must be installed before 168 running Kamailio with this module loaded: 169 * None 170 1713. Parameters 172 173 3.1. sipmsg (int) 174 3.2. cmap_size (int) 175 3.3. timer_interval (int) 176 3.4. auth_min_expires (int) 177 3.5. auth_max_expires (int) 178 3.6. use_path_addr (str) 179 3.7. event_callback (str) 180 1813.1. sipmsg (int) 182 183 If set to 1, the module will build a SIP message from MSRP frame 184 headers, providing it to “event_route[msrp:frame-in]”. All the config 185 file functions (apart from SIP request relay) that can be used in a 186 request route block can be used in the MSRP event_route. 187 188 Default value is '1'. 189 190 Example 1.1. Set sipmsg parameter 191... 192modparam("msrp", "sipmsg", 1) 193... 194 1953.2. cmap_size (int) 196 197 The size of connection map table, to be computed as power of 2 (e.g., 198 if the value is 4, then the number of slots in map table is 2^4 = 16). 199 200 Default value is '0' (no internal map table to be used). 201 202 Example 1.2. Set cmap_size parameter 203... 204modparam("msrp", "cmap_size", 8) 205... 206 2073.3. timer_interval (int) 208 209 The timer interval in seconds to run the procedure for cleaning expired 210 connections. 211 212 Default value is '60'. 213 214 Example 1.3. Set timer_interval parameter 215... 216modparam("msrp", "timer_interval", 90) 217... 218 2193.4. auth_min_expires (int) 220 221 The minimum value accepted for the “Expires” header in AUTH requests. 222 223 Default value is '60'. 224 225 Example 1.4. Set auth_min_expires parameter 226... 227modparam("msrp", "auth_min_expiresl", 90) 228... 229 2303.5. auth_max_expires (int) 231 232 The maximum value accepted for “Expires” header in AUTH requests. 233 234 Default value is '3600'. 235 236 Example 1.5. Set auth_max_expires parameter 237... 238modparam("msrp", "auth_max_expiresl", 1800) 239... 240 2413.6. use_path_addr (str) 242 243 The hostname:port to be used when building the Path header. 244 245 Default value is NULL (server IP and port are used). 246 247 Example 1.6. Set use_path_addr parameter 248... 249modparam("msrp", "use_path_addr", "msrp.kamailio.org:5061") 250... 251 2523.7. event_callback (str) 253 254 The name of the function in the kemi configuration file (embedded 255 scripting language such as Lua, Python, ...) to be executed instead of 256 event_route[...] blocks. 257 258 The function receives a string parameter with the name of the event, 259 the values can be: 'msrp:frame-in'. 260 261 Default value is 'empty' (no function is executed for events). 262 263 Example 1.7. Set event_callback parameter 264... 265modparam("msrp", "event_callback", "ksr_msrp_event") 266... 267-- event callback function implemented in Lua 268function ksr_msrp_event(evname) 269 KSR.info("===== msrp module triggered event: " .. evname .. "\n"); 270 return 1; 271end 272... 273 2744. Functions 275 276 4.1. msrp_relay() 277 4.2. msrp_reply(code, text [, hdrs]) 278 4.3. msrp_is_request() 279 4.4. msrp_is_reply() 280 4.5. msrp_set_dst(addr, sock) 281 4.6. msrp_relay_flags(flags) 282 4.7. msrp_reply_flags(flags) 283 4.8. msrp_cmap_save() 284 4.9. msrp_cmap_lookup() 285 2864.1. msrp_relay() 287 288 Relay MSRP frame according to the To-Path. This function has to be 289 executed for each MSRP request or reply that has to be forwarded. Note 290 that due to nature of the MSRP transport layer, which is reliable 291 (TCP/TLS), there is no retransmission of MSRP frames. 292 293 This function can be used in ANY_ROUTE. 294 295 Example 1.8. msrp_relay usage 296... 297event_route[msrp:frame-in] { 298 msrp_relay(); 299} 300... 301 3024.2. msrp_reply(code, text [, hdrs]) 303 304 Send a reply for the current MSRP request, adding optional headers. 305 306 The parameter can be a pseudo-variable. 307 308 This function can be used in ANY_ROUTE. 309 310 Example 1.9. msrp_reply usage 311... 312event_route[msrp:frame-in] { 313 msrp_reply("403", "Not allowed"); 314} 315... 316 3174.3. msrp_is_request() 318 319 Return true if the MSRP frame is a request. 320 321 This function can be used in ANY_ROUTE. 322 323 Example 1.10. msrp_is_request usage 324... 325event_route[msrp:frame-in] { 326 if(msrp_is_request()) 327 { 328 msrp_relay(); 329 exit; 330 } 331} 332... 333 3344.4. msrp_is_reply() 335 336 Return true if the MSRP frame is a reply. 337 338 This function can be used in ANY_ROUTE. 339 340 Example 1.11. msrp_is_reply usage 341... 342event_route[msrp:frame-in] { 343 if(msrp_is_reply()) 344 { 345 msrp_relay(); 346 exit; 347 } 348} 349... 350 3514.5. msrp_set_dst(addr, sock) 352 353 Set destination attributes: addr - target address as MSRP URI; sock - 354 local socket to be used (format 'proto:ip:port'). 355 356 The parameter can be a pseudo-variable. 357 358 This function can be used in ANY_ROUTE. 359 360 Example 1.12. msrp_set_dst usage 361... 362event_route[msrp:frame-in] { 363 ... 364 msrp_set_dst("msrp://127.0.0.1:8000", "tcp:127.0.0.1:5060"); 365 ... 366} 367... 368 3694.6. msrp_relay_flags(flags) 370 371 Set transport layer sending flags for forwarding current MSRP frame; 372 flags - a bitmask of flags - 1 (don't create a new connection), 2 373 (close connection after send). 374 375 The parameter can be a pseudo-variable. 376 377 This function can be used in ANY_ROUTE. 378 379 Example 1.13. msrp_relay_flags usage 380... 381event_route[msrp:frame-in] { 382 ... 383 msrp_relay_flags("1"); 384 ... 385} 386... 387 3884.7. msrp_reply_flags(flags) 389 390 Set transport layer sending flags for replies to the current MSRP 391 frame; flags - a bitmask of flags - 1 (don't create a new connection), 392 2 (close connection after send). 393 394 The parameter can be a pseudo-variable. 395 396 This function can be used in ANY_ROUTE. 397 398 Example 1.14. msrp_reply_flags usage 399... 400event_route[msrp:frame-in] { 401 ... 402 msrp_reply_flags("1"); 403 ... 404} 405... 406 4074.8. msrp_cmap_save() 408 409 Save details of a MSRP connection upon AUTH request inside the internal 410 map table, indexed by session id. 411 412 This function can be used in ANY_ROUTE. 413 414 Example 1.15. msrp_cmap_save usage 415... 416event_route[msrp:frame-in] { 417 ... 418 if(method=="AUTH") { msrp_cmap_save(); exit; } 419 ... 420} 421... 422 4234.9. msrp_cmap_lookup() 424 425 Lookup MSRP connection details for current session id. 426 427 This function can be used in ANY_ROUTE. 428 429 Example 1.16. msrp_cmap_lookup usage 430... 431event_route[msrp:frame-in] { 432 ... 433 if(method=="SEND" and $msrp(nexthops)==1) { 434 if(msrp_cmap_lookup()) { 435 msrp_relay(); 436 } else { 437 msrp_reply("481", "Session not found"); 438 } 439 } 440 ... 441} 442... 443 4445. Pseudo Variables 445 446 The module exports a pseudo-variable class, $msrp(key), to access the 447 MSRP frame (e.g. first line attributes, body, all frame content). 448 449 The module exports a transformations class, 'msrpuri', to allow access 450 attributes of a MSRP URI. 451 452 These are documented in the appropriate Wiki pages hosted on the 453 project web site. 454 4556. RPC Commands 456 457 6.1. msrp.cmaplist 458 4596.1. msrp.cmaplist 460 461 List active MSRP connections. 462 463 Example: 464... 465kamcmd msrp.cmaplist 466... 467 4687. Event Routes 469 470 For each MSRP frame received from the network, the module executes 471 event_route[msrp:frame-in] block in the config file. 472 4738. Usage 474 475 When the sipmsg parameter is set to 1 (which is the default), the 476 module internally builds a SIP request from the MSRP frame and exposes 477 it to the config file interpreter. This way, all the functions that are 478 valid for SIP requests can be used safely in 479 event_route[msrp:frame-in]. 480 481 To build the SIP request, the module takes the first line and the 482 headers from an MSRP message and appends them to a static buffer. The 483 next two examples show an MSRP frame and the resulting SIP request. 484... 485MSRP 6aef SEND 486To-Path: msrps://a.example.org:9000/kjfjan;tcp \ 487 msrps://b.example.net:9000/aeiug;tcp \ 488 msrps://bob.example.net:8145/foo;tcp 489From-Path: msrps://alice.example.org:7965/bar;tcp 490Success-Report: yes 491Byte-Range: 1-*/* 492Message-ID: 87652 493Content-Type: text/plain 494 495Hi Bob, I'm about to send you a photo. 496-------6aef$ 497... 498... 499MSRP sip:a@127.0.0.1 SIP/2.0 500Via: SIP/2.0/UDP 127.0.0.1:9;branch=z9hG4bKa 501From: <b@127.0.0.1>;tag=a 502To: <a@127.0.0.1> 503Call-ID: a 504CSeq: 1 MSRP 505Content-Length: 0 506MSRP-First-Line: MSRP 6aef SEND 507To-Path: msrps://a.example.org:9000/kjfjan;tcp \ 508 msrps://b.example.net:9000/aeiug;tcp \ 509 msrps://bob.example.net:8145/foo;tcp 510From-Path: msrps://alice.example.org:7965/bar;tcp 511Success-Report: yes 512Byte-Range: 1-*/* 513Message-ID: 87652 514Content-Type: text/plain 515 516... 517 518 Note that MSRP does not permit line folding. A "\" in the examples 519 shows a line continuation due to the limitations of line length of this 520 document. Neither the backslash nor the extra CRLF is included in the 521 actual request or response. 522 523 As can be observed, the MSRP frame content starts with the body of the 524 'MSRP-First-Line:' header. Using static content to get to a valid SIP 525 request is a perfect trade-off for performance. 526 527 Besides the option to access parts of MSRP frame via an 528 internally-built SIP message, the module exports a new pseudo-variable 529 class $msrp(key) which returns attributes from the MSRP frame. There is 530 also a new transformation, {msrpuri.key}, to get access to parts of an 531 MSRP URI. See the appropriate Wiki pages on the project's web site for 532 full details about new pseudo-variable and transformation classes. 533 534 Next is an example of configuration file with the routing block for 535 MSRP frames. In this config, the SIP traffic is rejected. 536 537 Example 1.17. Event Route (using htable for MSRP connection tracking) 538... 539 540#!KAMAILIO 541 542debug=2 543memdbg=5 544memlog=5 545 546children=4 547 548log_stderror=yes 549auto_aliases=no 550 551tcp_accept_no_cl=yes 552tcp_connection_lifetime=1810 553 554listen=127.0.0.1:5060 555 556mpath="modules_k/:modules/" 557 558loadmodule "sl.so" 559loadmodule "kex.so" 560loadmodule "mi_fifo.so" 561loadmodule "ctl.so" 562loadmodule "msrp.so" 563loadmodule "pv.so" 564loadmodule "auth.so" 565loadmodule "cfgutils.so" 566loadmodule "htable.so" 567loadmodule "xlog.so" 568 569modparam("mi_fifo", "fifo_name", "/tmp/kamailio_fifo") 570 571modparam("auth", "nonce_count", 1) 572modparam("auth", "qop", "auth") 573 574#!substdef "!MSRP_MIN_EXPIRES!1800!g" 575#!substdef "!MSRP_MAX_EXPIRES!3600!g" 576modparam("htable", "htable", "msrp=>size=8;autoexpire=MSRP_MAX_EXPIRES;") 577 578request_route { 579 sl_send_reply("403", "No SIP Here"); 580 exit; 581} 582 583reply_route { 584 drop; 585} 586 587event_route[msrp:frame-in] { 588 xdbg("============#[[$msrp(method)]]===========\n"); 589 xdbg("============*[[$si:$sp]]\n"); 590 xdbg("============ crthop: [$msrp(crthop)]\n"); 591 xdbg("============ prevhop: [$msrp(prevhop)]\n"); 592 xdbg("============ nexthop: [$msrp(nexthop)]\n"); 593 xdbg("============ firsthop: [$msrp(firsthop)]\n"); 594 xdbg("============ lasthop: [$msrp(lasthop)]\n"); 595 xdbg("============ prevhops: [$msrp(prevhops)]\n"); 596 xdbg("============ nexthops: [$msrp(nexthops)]\n"); 597 xdbg("============ srcaddr: [$msrp(srcaddr)]\n"); 598 xdbg("============ srcsock: [$msrp(srcsock)]\n"); 599 xdbg("============ sessid: [$msrp(sessid)]\n"); 600 601 msrp_reply_flags("1"); 602 603 if(msrp_is_reply()) 604 { 605 msrp_relay(); 606 } 607 else if($msrp(method)=="AUTH") 608 { 609 if($msrp(nexthops)>0) 610 { 611 msrp_relay(); 612 exit; 613 } 614 615 # frame for local server - send Use-Path 616 # -- passwd can be loaded from DB based on $au 617 $var(passwd) = "xyz123"; 618 if(!pv_www_authenticate("myrealm", "$var(passwd)", "0", 619 "$msrp(method)")) 620 { 621 if(auth_get_www_authenticate("myrealm", "1", 622 "$var(wauth)")) 623 { 624 msrp_reply("401", "Unauthorized", 625 "$var(wauth)"); 626 } else { 627 msrp_reply("500", "Server Error"); 628 } 629 exit; 630 } 631 632 if ($hdr(Expires) != $null) { 633 $var(expires) = (int) $hdr(Expires); 634 if ($var(expires) < MSRP_MIN_EXPIRES) { 635 msrp_reply("423", "Interval Out-of-Bounds", 636 "Min-Expires: MSRP_MIN_EXPIRES\r\n"); 637 exit; 638 } else { 639 msrp_reply("423", "Interval Out-of-Bounds", 640 "Max-Expires: MSRP_MAX_EXPIRES\r\n"); 641 exit; 642 } 643 644 } else 645 $var(expires) = MSRP_MAX_EXPIRES; 646 647 $var(cnt) = $var(cnt) + 1; 648 pv_printf("$var(sessid)", "s.$(pp).$(var(cnt)).$(RANDOM)"); 649 $sht(msrp=>$var(sessid)::srcaddr) = $msrp(srcaddr); 650 $sht(msrp=>$var(sessid)::srcsock) = $msrp(srcsock); 651 $shtex(msrp=>$var(sessid)) = $var(expires) + 5; 652 # - Use-Path: the MSRP address for server + session id 653 $var(headers) = "Use-Path: msrp://127.0.0.1:5060/" 654 + $var(sessid) + ";tcp\r\n" 655 + "Expires: " + $var(expires) + "\r\n"; 656 msrp_reply("200", "OK", "$var(headers)"); 657 } 658 else if($msrp(method)=="SEND" || $msrp(method)=="REPORT") 659 { 660 if($msrp(nexthops)>1) 661 { 662 if ($msrp(method)!="REPORT") 663 { 664 msrp_reply("200", "OK"); 665 } 666 msrp_relay(); 667 exit; 668 } 669 $var(sessid) = $msrp(sessid); 670 if($sht(msrp=>$var(sessid)::srcaddr) == $null) 671 { 672 # one more hop, but we don't have address in htable 673 if ($msrp(method)!="REPORT") 674 { 675 msrp_reply("481", "Session-does-not-exist"); 676 } 677 exit; 678 } 679 else if($msrp(method)!="REPORT") 680 { 681 msrp_reply("200", "OK"); 682 } 683 msrp_relay_flags("1"); 684 msrp_set_dst("$sht(msrp=>$var(sessid)::srcaddr)", 685 "$sht(msrp=>$var(sessid)::srcsock)"); 686 msrp_relay(); 687 } 688 else 689 { 690 msrp_reply("501", "Request-method-not-understood"); 691 } 692} 693 694... 695 696 Example 1.18. Event Route (using msrp_cmap_ functions for connection 697 tracking) 698... 699 700#!KAMAILIO 701 702debug=2 703memdbg=5 704memlog=5 705 706children=4 707 708log_stderror=yes 709auto_aliases=no 710 711tcp_accept_no_cl=yes 712tcp_connection_lifetime=1810 713 714listen=127.0.0.1:5060 715 716mpath="modules_k/:modules/" 717 718loadmodule "sl.so" 719loadmodule "kex.so" 720loadmodule "mi_fifo.so" 721loadmodule "ctl.so" 722loadmodule "msrp.so" 723loadmodule "pv.so" 724loadmodule "auth.so" 725loadmodule "cfgutils.so" 726loadmodule "xlog.so" 727 728modparam("mi_fifo", "fifo_name", "/tmp/kamailio_fifo") 729 730modparam("auth", "nonce_count", 1) 731modparam("auth", "qop", "auth") 732 733modparam("msrp", "cmap_size", 8) 734modparam("msrp", "use_path_addr", "msrp://127.0.0.1:5060") 735 736request_route { 737 sl_send_reply("403", "No SIP Here"); 738 exit; 739} 740 741reply_route { 742 drop; 743} 744 745event_route[msrp:frame-in] { 746 xdbg("============#[[$msrp(method)]]===========\n"); 747 xdbg("============*[[$si:$sp]]\n"); 748 xdbg("============ crthop: [$msrp(crthop)]\n"); 749 xdbg("============ prevhop: [$msrp(prevhop)]\n"); 750 xdbg("============ nexthop: [$msrp(nexthop)]\n"); 751 xdbg("============ firsthop: [$msrp(firsthop)]\n"); 752 xdbg("============ lasthop: [$msrp(lasthop)]\n"); 753 xdbg("============ prevhops: [$msrp(prevhops)]\n"); 754 xdbg("============ nexthops: [$msrp(nexthops)]\n"); 755 xdbg("============ srcaddr: [$msrp(srcaddr)]\n"); 756 xdbg("============ srcsock: [$msrp(srcsock)]\n"); 757 xdbg("============ sessid: [$msrp(sessid)]\n"); 758 759 msrp_reply_flags("1"); 760 761 if (msrp_is_reply()) 762 { 763 msrp_relay(); 764 } 765 else if($msrp(method)=="AUTH") 766 { 767 if($msrp(nexthops)>0) 768 { 769 msrp_relay(); 770 exit; 771 } 772 773 # frame for local server - send Use-Path 774 # -- passwd can be loaded from DB based on $au 775 $var(passwd) = "xyz123"; 776 if(!pv_www_authenticate("myrealm", "$var(passwd)", "0", 777 "$msrp(method)")) 778 { 779 if(auth_get_www_authenticate("myrealm", "1", 780 "$var(wauth)")) 781 { 782 msrp_reply("401", "Unauthorized", 783 "$var(wauth)"); 784 } else { 785 msrp_reply("500", "Server Error"); 786 } 787 exit; 788 } 789 790 msrp_cmap_save(); 791 } 792 else if ($msrp(method)=="SEND" || $msrp(method)=="REPORT") 793 { 794 if ($msrp(nexthops)>1) 795 { 796 if ($msrp(method)!="REPORT") 797 { 798 msrp_reply("200", "OK"); 799 } 800 msrp_relay(); 801 exit; 802 } 803 804 if (msrp_cmap_lookup()) 805 { 806 if ($msrp(method)!="REPORT") 807 { 808 msrp_reply("200", "OK"); 809 } 810 msrp_relay_flags("1"); 811 msrp_relay(); 812 } 813 else 814 { 815 msrp_reply("481", "Session-does-not-exist"); 816 } 817 } 818 else 819 { 820 msrp_reply("501", "Request-method-not-understood"); 821 } 822} 823 824... 825