... | ... |
@@ -127,8 +127,13 @@ class SIPRegistration : public AmSipDialogEventHandler, |
127 | 127 |
void onSipReply(const AmSipReply& reply, int old_dlg_status); |
128 | 128 |
void onSipRequest(const AmSipRequest& req) {} |
129 | 129 |
void onInvite2xx(const AmSipReply&) {} |
130 |
+#if 0 |
|
130 | 131 |
void onNo2xxACK(unsigned int) {} |
131 | 132 |
void onNoErrorACK(unsigned int) {} |
133 |
+#else |
|
134 |
+ void onNoAck(unsigned int) {} |
|
135 |
+ void onNoPrack(unsigned int) {} |
|
136 |
+#endif |
|
132 | 137 |
|
133 | 138 |
/** is this registration registered? */ |
134 | 139 |
bool active; |
... | ... |
@@ -36,6 +36,7 @@ |
36 | 36 |
#include "log.h" |
37 | 37 |
#include "AmConfigReader.h" |
38 | 38 |
#include "AmUtils.h" |
39 |
+#include "AmSession.h" |
|
39 | 40 |
|
40 | 41 |
#include <fstream> |
41 | 42 |
#include <cctype> |
... | ... |
@@ -89,6 +90,8 @@ unsigned int AmConfig::OptionsSessionLimit = 0; |
89 | 90 |
unsigned int AmConfig::OptionsSessionLimitErrCode = 503; |
90 | 91 |
string AmConfig::OptionsSessionLimitErrReason = "Server overload"; |
91 | 92 |
|
93 |
+unsigned char AmConfig::rel100 = REL100_SUPPORTED; |
|
94 |
+ |
|
92 | 95 |
vector <string> AmConfig::CodecOrder; |
93 | 96 |
|
94 | 97 |
Dtmf::InbandDetectorType |
... | ... |
@@ -519,5 +522,20 @@ int AmConfig::readConfiguration() |
519 | 522 |
} |
520 | 523 |
} |
521 | 524 |
|
525 |
+ if (cfg.hasParameter("100rel")) { |
|
526 |
+ string rel100s = cfg.getParameter("100rel"); |
|
527 |
+ if (rel100s == "disabled" || rel100s == "off") { |
|
528 |
+ rel100 = REL100_DISABLED; |
|
529 |
+ } else if (rel100s == "supported") { |
|
530 |
+ rel100 = REL100_SUPPORTED; |
|
531 |
+ } else if (rel100s == "require") { |
|
532 |
+ rel100 = REL100_REQUIRE; |
|
533 |
+ } else { |
|
534 |
+ ERROR("unknown setting for '100rel' config option.\n"); |
|
535 |
+ return -1; |
|
536 |
+ } |
|
537 |
+ } |
|
538 |
+ INFO("100rel: %d.\n", AmConfig::rel100); |
|
539 |
+ |
|
522 | 540 |
return 0; |
523 | 541 |
} |
... | ... |
@@ -135,6 +135,8 @@ struct AmConfig |
135 | 135 |
static unsigned int OptionsSessionLimitErrCode; |
136 | 136 |
static string OptionsSessionLimitErrReason; |
137 | 137 |
|
138 |
+ static unsigned char rel100; |
|
139 |
+ |
|
138 | 140 |
/** Time of no RTP after which Session is regarded as dead, 0 for no Timeout */ |
139 | 141 |
static unsigned int DeadRtpTime; |
140 | 142 |
|
... | ... |
@@ -36,6 +36,7 @@ |
36 | 36 |
#include "AmMediaProcessor.h" |
37 | 37 |
#include "AmDtmfDetector.h" |
38 | 38 |
#include "AmPlayoutBuffer.h" |
39 |
+#include "AmSipHeaders.h" |
|
39 | 40 |
|
40 | 41 |
#ifdef WITH_ZRTP |
41 | 42 |
#include "AmZRTP.h" |
... | ... |
@@ -49,6 +50,7 @@ |
49 | 50 |
#include <assert.h> |
50 | 51 |
#include <sys/time.h> |
51 | 52 |
|
53 |
+ |
|
52 | 54 |
volatile unsigned int AmSession::session_num = 0; |
53 | 55 |
AmMutex AmSession::session_num_mut; |
54 | 56 |
|
... | ... |
@@ -65,6 +67,7 @@ AmSession::AmSession() |
65 | 67 |
m_dtmfDetector(this), m_dtmfEventQueue(&m_dtmfDetector), |
66 | 68 |
m_dtmfDetectionEnabled(true), |
67 | 69 |
accept_early_session(false), |
70 |
+ reliable_1xx(AmConfig::rel100), |
|
68 | 71 |
processing_status(SESSION_PROCESSING_EVENTS) |
69 | 72 |
#ifdef WITH_ZRTP |
70 | 73 |
, zrtp_session(NULL), zrtp_audio(NULL), enable_zrtp(true) |
... | ... |
@@ -76,6 +79,8 @@ AmSession::AmSession() |
76 | 79 |
{ |
77 | 80 |
use_local_audio[AM_AUDIO_IN] = false; |
78 | 81 |
use_local_audio[AM_AUDIO_OUT] = false; |
82 |
+ if (reliable_1xx) |
|
83 |
+ dlg.rseq = 0; //??? |
|
79 | 84 |
} |
80 | 85 |
|
81 | 86 |
AmSession::~AmSession() |
... | ... |
@@ -668,8 +673,41 @@ void AmSession::onSipRequest(const AmSipRequest& req) |
668 | 673 |
CALL_EVENT_H(onSipRequest,req); |
669 | 674 |
|
670 | 675 |
DBG("onSipRequest: method = %s\n",req.method.c_str()); |
671 |
- if(req.method == "INVITE"){ |
|
672 |
- |
|
676 |
+ if(req.method == SIP_METH_INVITE){ |
|
677 |
+ |
|
678 |
+ switch(reliable_1xx) { |
|
679 |
+ case REL100_SUPPORTED: /* if support is on, enforce if asked by UAC */ |
|
680 |
+ if (key_in_list(getHeader(req.hdrs, SIP_HDR_SUPPORTED), |
|
681 |
+ SIP_EXT_100REL) || |
|
682 |
+ key_in_list(getHeader(req.hdrs, SIP_HDR_REQUIRE), |
|
683 |
+ SIP_EXT_100REL)) { |
|
684 |
+ reliable_1xx = REL100_REQUIRE; |
|
685 |
+ DBG(SIP_EXT_100REL " now active.\n"); |
|
686 |
+ } |
|
687 |
+ break; |
|
688 |
+ case REL100_REQUIRE: /* if support is required, reject if UAC doesn't */ |
|
689 |
+ if (! (key_in_list(getHeader(req.hdrs,SIP_HDR_SUPPORTED), |
|
690 |
+ SIP_EXT_100REL) || |
|
691 |
+ key_in_list(getHeader(req.hdrs, SIP_HDR_REQUIRE), |
|
692 |
+ SIP_EXT_100REL))) { |
|
693 |
+ ERROR("'" SIP_EXT_100REL "' extension required, but not advertised" |
|
694 |
+ " by peer.\n"); |
|
695 |
+ dlg.reply(req, 421, "Extension Required", "", "", |
|
696 |
+ SIP_HDR_COLSP(SIP_HDR_REQUIRE) SIP_EXT_100REL CRLF); |
|
697 |
+ if (dlg.getStatus() < AmSipDialog::Connected) |
|
698 |
+ setStopped(); |
|
699 |
+ return; |
|
700 |
+ } |
|
701 |
+ default: |
|
702 |
+ ERROR("BUG: unexpected value `%d' for '" SIP_EXT_100REL "' switch.", |
|
703 |
+ reliable_1xx); |
|
704 |
+#ifndef NDEBUG |
|
705 |
+ abort(); |
|
706 |
+#endif |
|
707 |
+ case 0: /* support disabled */ |
|
708 |
+ break; |
|
709 |
+ } |
|
710 |
+ |
|
673 | 711 |
onInvite(req); |
674 | 712 |
|
675 | 713 |
if(detached.get() && !getStopped()){ |
... | ... |
@@ -702,9 +740,25 @@ void AmSession::onSipRequest(const AmSipRequest& req) |
702 | 740 |
} else { |
703 | 741 |
dlg.reply(req, 415, "Unsupported Media Type"); |
704 | 742 |
} |
705 |
- } |
|
706 |
-} |
|
743 |
+ } else if( req.method == SIP_METH_PRACK ) { |
|
744 |
+ if (reliable_1xx != REL100_REQUIRE) { |
|
745 |
+ WARN("unexpected PRACK received while " SIP_EXT_100REL " not active.\n"); |
|
746 |
+ return; |
|
747 |
+ } |
|
707 | 748 |
|
749 |
+ if ((1<<MAX_RSEQ_BITS)<=req.rseq && req.rseq<=(unsigned)abs(dlg.rseq)) { |
|
750 |
+ // call interface function |
|
751 |
+ onPrack(req, LOC_RSEQ_ORDER(req.rseq)); |
|
752 |
+ if (req.rseq == (unsigned)-dlg.rseq) { |
|
753 |
+ dlg.rseq = -dlg.rseq; // confirmed |
|
754 |
+ DBG("latest RSeq (%u) confirmed.\n", dlg.rseq); |
|
755 |
+ } |
|
756 |
+ } else { |
|
757 |
+ WARN("no matching RAck value in PRACK (%s).\n", req.hdrs.c_str()); |
|
758 |
+ return; |
|
759 |
+ } |
|
760 |
+ } |
|
761 |
+} |
|
708 | 762 |
|
709 | 763 |
void AmSession::onSipReply(const AmSipReply& reply, int old_dlg_status) |
710 | 764 |
{ |
... | ... |
@@ -755,7 +809,7 @@ void AmSession::onSipReply(const AmSipReply& reply, int old_dlg_status) |
755 | 809 |
break; |
756 | 810 |
|
757 | 811 |
case AmSipDialog::Pending: |
758 |
- |
|
812 |
+ |
|
759 | 813 |
switch(reply.code){ |
760 | 814 |
// todo: 180 with body (remote rbt) |
761 | 815 |
case 180: { |
... | ... |
@@ -793,17 +847,61 @@ void AmSession::onSipReply(const AmSipReply& reply, int old_dlg_status) |
793 | 847 |
} break; |
794 | 848 |
default: break;// continue waiting. |
795 | 849 |
} |
796 |
- } |
|
797 |
- } |
|
798 |
- } |
|
850 |
+ |
|
851 |
+ // FIXME: should this stay under negotiate_onreply??? |
|
852 |
+ if (100 < reply.code && reply.code < 200 && |
|
853 |
+ reply.method == SIP_METH_INVITE) { |
|
854 |
+ switch (reliable_1xx) { |
|
855 |
+ case REL100_SUPPORTED: |
|
856 |
+ if (key_in_list(getHeader(reply.hdrs, SIP_HDR_REQUIRE), |
|
857 |
+ SIP_EXT_100REL)) |
|
858 |
+ reliable_1xx = REL100_REQUIRE; |
|
859 |
+ else |
|
860 |
+ break; |
|
861 |
+ |
|
862 |
+ case REL100_REQUIRE: { |
|
863 |
+ if (! reply.rseq) { |
|
864 |
+ ERROR("no RSeq value (or unsupported 0) in reliable 1xx.\n"); |
|
865 |
+ dlg.cancel(); |
|
866 |
+ setStopped(); |
|
867 |
+ break; |
|
868 |
+ } |
|
869 |
+ string cseq_val = int2str(reply.cseq) + " " + reply.method; |
|
870 |
+ sendPrack(/*rcvd sdp_offr [TODO]*/"",int2str(reply.rseq),cseq_val); |
|
871 |
+ DBG(SIP_EXT_100REL " now active.\n"); |
|
872 |
+ } |
|
873 |
+ break; |
|
874 |
+ |
|
875 |
+ case 0: |
|
876 |
+ // 100rel support disabled |
|
877 |
+ break; |
|
878 |
+ default: |
|
879 |
+ ERROR("BUG: unexpected value `%d' for " SIP_EXT_100REL " switch.", |
|
880 |
+ reliable_1xx); |
|
881 |
+#ifndef NDEBUG |
|
882 |
+ abort(); |
|
883 |
+#endif |
|
884 |
+ } // switch reliable 1xx |
|
885 |
+ } else if (300<=reply.code && reliable_1xx && |
|
886 |
+ reply.method==SIP_METH_PRACK) { |
|
887 |
+ // if PRACK fails, tear down session |
|
888 |
+ dlg.cancel(); |
|
889 |
+ setStopped(); |
|
890 |
+ } // if 1xx && INVITE || failed && PRACK |
|
891 |
+ } // switch dlg status |
|
892 |
+ } // status < Connected |
|
893 |
+ } //if negotiate_onreply |
|
799 | 894 |
} |
800 | 895 |
|
896 |
+ |
|
897 |
+ |
|
801 | 898 |
void AmSession::onInvite2xx(const AmSipReply& reply) |
802 | 899 |
{ |
803 | 900 |
AmSipTransaction* t = dlg.get_uac_trans(reply.cseq); |
804 | 901 |
if(t) dlg.send_200_ack(*t); |
805 | 902 |
} |
806 | 903 |
|
904 |
+#if 0 |
|
807 | 905 |
void AmSession::onNo2xxACK(unsigned int cseq) |
808 | 906 |
{ |
809 | 907 |
dlg.bye(); |
... | ... |
@@ -814,6 +912,29 @@ void AmSession::onNoErrorACK(unsigned int cseq) |
814 | 912 |
{ |
815 | 913 |
setStopped(); |
816 | 914 |
} |
915 |
+#else |
|
916 |
+void AmSession::onNoAck(unsigned int cseq) |
|
917 |
+{ |
|
918 |
+ if (dlg.getStatus() == AmSipDialog::Connected) |
|
919 |
+ dlg.bye(); |
|
920 |
+ setStopped(); |
|
921 |
+} |
|
922 |
+ |
|
923 |
+void AmSession::onNoPrack(const AmSipRequest &req, const AmSipReply &rpl) |
|
924 |
+{ |
|
925 |
+ INFO("reply <%s> timed out.\n", rpl.print().c_str()); |
|
926 |
+ if (100 < rpl.code && rpl.code < 200 && reliable_1xx == REL100_REQUIRE && |
|
927 |
+ (unsigned)dlg.rseq == rpl.rseq && rpl.method == SIP_METH_INVITE) { |
|
928 |
+ INFO("reliable %d reply timed out; rejecting request.\n", rpl.code); |
|
929 |
+ dlg.reply(req, 504, "Server Time-out"); |
|
930 |
+ if (dlg.getStatus() < AmSipDialog::Connected) |
|
931 |
+ setStopped(); |
|
932 |
+ } else { |
|
933 |
+ WARN("reply timed-out, but not reliable.\n"); // debugging |
|
934 |
+ } |
|
935 |
+} |
|
936 |
+ |
|
937 |
+#endif |
|
817 | 938 |
|
818 | 939 |
void AmSession::onAudioEvent(AmAudioEvent* audio_ev) |
819 | 940 |
{ |
... | ... |
@@ -844,6 +965,12 @@ void AmSession::onBye(const AmSipRequest& req) |
844 | 965 |
setStopped(); |
845 | 966 |
} |
846 | 967 |
|
968 |
+void AmSession::onPrack(const AmSipRequest& req, unsigned cnt) |
|
969 |
+{ |
|
970 |
+ DBG("handling #%u PRACK.\n", cnt); |
|
971 |
+ dlg.reply(req, 200, "OK"); |
|
972 |
+} |
|
973 |
+ |
|
847 | 974 |
int AmSession::acceptAudio(const string& body, |
848 | 975 |
const string& hdrs, |
849 | 976 |
string* sdp_reply) |
... | ... |
@@ -906,6 +1033,47 @@ void AmSession::onSendReply(const AmSipRequest& req, unsigned int code, |
906 | 1033 |
const string& reason, const string& content_type, |
907 | 1034 |
const string& body, string& hdrs, int flags) |
908 | 1035 |
{ |
1036 |
+ if (req.method == SIP_METH_INVITE) { |
|
1037 |
+ if (100 < code && code < 200) { |
|
1038 |
+ switch (reliable_1xx) { |
|
1039 |
+ case REL100_SUPPORTED: |
|
1040 |
+ hdrs += SIP_HDR_COLSP(SIP_HDR_SUPPORTED) SIP_EXT_100REL CRLF; |
|
1041 |
+ break; |
|
1042 |
+ case REL100_REQUIRE: |
|
1043 |
+ // add Require HF |
|
1044 |
+ hdrs += SIP_HDR_COLSP(SIP_HDR_REQUIRE) SIP_EXT_100REL CRLF; |
|
1045 |
+ // add RSeq HF |
|
1046 |
+#ifndef NDEBUG |
|
1047 |
+ if ((abs(dlg.rseq) & ((1 << MAX_RSEQ_BITS) - 1)) == |
|
1048 |
+ ((1 << MAX_RSEQ_BITS) - 1)) { |
|
1049 |
+ ERROR("CRITICAL: RSeq value too high: increase MAX_RSEQ_BITS " |
|
1050 |
+ "(now %d) and recompile.\n", MAX_RSEQ_BITS); |
|
1051 |
+ abort(); |
|
1052 |
+ } |
|
1053 |
+#endif |
|
1054 |
+ if (dlg.rseq < 0) { // RSeq not yet PRACKed |
|
1055 |
+ // refuse subsequent 1xx if first isn't yet PRACKed |
|
1056 |
+ if ((((unsigned)-dlg.rseq) & ((1 << MAX_RSEQ_BITS) - 1)) == 0) |
|
1057 |
+ throw AmSession::Exception(491, "last reliable 1xx not yet " |
|
1058 |
+ "PRACKed"); |
|
1059 |
+ dlg.rseq --; |
|
1060 |
+ } else if (! dlg.rseq) { // only init rseq if 1xx is used |
|
1061 |
+ unsigned rseq_1st = (get_random() + 1) << MAX_RSEQ_BITS; |
|
1062 |
+ rseq_1st &= 0x7fffffff; |
|
1063 |
+ dlg.rseq = -((signed)rseq_1st); |
|
1064 |
+ } else { |
|
1065 |
+ dlg.rseq = -(++dlg.rseq); |
|
1066 |
+ } |
|
1067 |
+ // FIXME: code above is not re-entrant; should it actually be??? |
|
1068 |
+ hdrs += SIP_HDR_COLSP(SIP_HDR_RSEQ) + int2str(-dlg.rseq) + CRLF; |
|
1069 |
+ break; |
|
1070 |
+ } |
|
1071 |
+ } else if (code < 300 && reliable_1xx == REL100_REQUIRE) { |
|
1072 |
+ if (dlg.rseq < 0) // reliable 1xx is pending |
|
1073 |
+ throw AmSession::Exception(491, "last reliable 1xx not yet PRACKed"); |
|
1074 |
+ } |
|
1075 |
+ } |
|
1076 |
+ |
|
909 | 1077 |
CALL_EVENT_H(onSendReply,req,code,reason,content_type,body,hdrs,flags); |
910 | 1078 |
} |
911 | 1079 |
|
... | ... |
@@ -915,9 +1083,47 @@ void AmSession::onRtpTimeout() |
915 | 1083 |
setStopped(); |
916 | 1084 |
} |
917 | 1085 |
|
918 |
-void AmSession::sendUpdate() |
|
1086 |
+void AmSession::sendUpdate(string &cont_type, string &body, string &hdrs) |
|
1087 |
+{ |
|
1088 |
+ dlg.update(cont_type, body, hdrs); |
|
1089 |
+} |
|
1090 |
+ |
|
1091 |
+void AmSession::sendPrack(const string &sdp_offer, |
|
1092 |
+ const string &rseq_val, |
|
1093 |
+ const string &cseq_val) |
|
1094 |
+{ |
|
1095 |
+ string hdrs = "RAck: " + rseq_val + " " + cseq_val + "\r\n"; |
|
1096 |
+ |
|
1097 |
+ // TODO: digest an answer based on the sdp_offer |
|
1098 |
+ |
|
1099 |
+ // TODO: should't cseq&rseq be handled in dialog, entirely?!?! |
|
1100 |
+ if (dlg.prack(/*cont. type*/"", /*body*/"", hdrs) < 0) |
|
1101 |
+ ERROR("failed to send PRACK request in session '%s'.\n",sid4dbg().c_str()); |
|
1102 |
+} |
|
1103 |
+ |
|
1104 |
+string AmSession::sid4dbg() |
|
919 | 1105 |
{ |
920 |
- dlg.update(""); |
|
1106 |
+ string dbg; |
|
1107 |
+ dbg = dlg.callid + "/" + dlg.local_tag + "/" + dlg.remote_tag + "/" + |
|
1108 |
+ int2str(RTPStream()->getLocalPort()) + "/" + |
|
1109 |
+ RTPStream()->getRHost() + ":" + int2str(RTPStream()->getRPort()); |
|
1110 |
+ return dbg; |
|
1111 |
+} |
|
1112 |
+ |
|
1113 |
+static inline string get_100rel_hdr(unsigned char reliable_1xx) |
|
1114 |
+{ |
|
1115 |
+ switch(reliable_1xx) { |
|
1116 |
+ case REL100_SUPPORTED: |
|
1117 |
+ return SIP_HDR_COLSP(SIP_HDR_SUPPORTED) SIP_EXT_100REL CRLF; |
|
1118 |
+ case REL100_REQUIRE: |
|
1119 |
+ return SIP_HDR_COLSP(SIP_HDR_REQUIRE) SIP_EXT_100REL CRLF; |
|
1120 |
+ default: |
|
1121 |
+ ERROR("BUG: unexpected reliability switch value of '%d'.\n", |
|
1122 |
+ reliable_1xx); |
|
1123 |
+ case 0: |
|
1124 |
+ break; |
|
1125 |
+ } |
|
1126 |
+ return ""; |
|
921 | 1127 |
} |
922 | 1128 |
|
923 | 1129 |
void AmSession::sendReinvite(bool updateSDP, const string& headers) |
... | ... |
@@ -926,9 +1132,10 @@ void AmSession::sendReinvite(bool updateSDP, const string& headers) |
926 | 1132 |
RTPStream()->setLocalIP(AmConfig::LocalIP); |
927 | 1133 |
string sdp_body; |
928 | 1134 |
sdp.genResponse(advertisedIP(), RTPStream()->getLocalPort(), sdp_body); |
929 |
- dlg.reinvite(headers, "application/sdp", sdp_body); |
|
1135 |
+ dlg.reinvite(headers + get_100rel_hdr(reliable_1xx), "application/sdp", |
|
1136 |
+ sdp_body); |
|
930 | 1137 |
} else { |
931 |
- dlg.reinvite(headers, "", ""); |
|
1138 |
+ dlg.reinvite(headers + get_100rel_hdr(reliable_1xx), "", ""); |
|
932 | 1139 |
} |
933 | 1140 |
} |
934 | 1141 |
|
... | ... |
@@ -943,7 +1150,8 @@ int AmSession::sendInvite(const string& headers) |
943 | 1150 |
// Generate SDP. |
944 | 1151 |
string sdp_body; |
945 | 1152 |
sdp.genRequest(advertisedIP(), RTPStream()->getLocalPort(), sdp_body); |
946 |
- return dlg.invite(headers, "application/sdp", sdp_body); |
|
1153 |
+ return dlg.invite(headers + get_100rel_hdr(reliable_1xx), "application/sdp", |
|
1154 |
+ sdp_body); |
|
947 | 1155 |
} |
948 | 1156 |
|
949 | 1157 |
void AmSession::setOnHold(bool hold) |
... | ... |
@@ -61,6 +61,14 @@ class AmDtmfEvent; |
61 | 61 |
#define AM_AUDIO_IN 0 |
62 | 62 |
#define AM_AUDIO_OUT 1 |
63 | 63 |
|
64 |
+ |
|
65 |
+/* maximum number of reliable 1xx supported; needed for marking first 1xx |
|
66 |
+ * (which can not be followed by subsequent ones w/o being PRACKed), as |
|
67 |
+ * opposed to having another value storing it's value. */ |
|
68 |
+#define MAX_RSEQ_BITS 24 |
|
69 |
+#define LOC_RSEQ_ORDER(_rseq) (_rseq & ((1 << MAX_RSEQ_BITS) - 1)) |
|
70 |
+ |
|
71 |
+ |
|
64 | 72 |
/** |
65 | 73 |
* \brief Implements the default behavior of one session |
66 | 74 |
* |
... | ... |
@@ -156,6 +164,12 @@ protected: |
156 | 164 |
|
157 | 165 |
/** do accept early session? */ |
158 | 166 |
bool accept_early_session; |
167 |
+ /** enable the reliability of provisional replies? */ |
|
168 |
+ unsigned char reliable_1xx; |
|
169 |
+#define REL100_DISABLED 0 |
|
170 |
+#define REL100_SUPPORTED 1 |
|
171 |
+#define REL100_REQUIRE 2 |
|
172 |
+#define REL100_MAX REL100_REQUIRE |
|
159 | 173 |
|
160 | 174 |
vector<AmSessionEventHandler*> ev_handlers; |
161 | 175 |
|
... | ... |
@@ -329,11 +343,15 @@ public: |
329 | 343 |
string* sdp_reply); |
330 | 344 |
|
331 | 345 |
/** send an UPDATE in the session */ |
332 |
- virtual void sendUpdate(); |
|
346 |
+ void sendUpdate(string &cont_type, string &body, string &hdrs); |
|
333 | 347 |
/** send a Re-INVITE (if connected) */ |
334 | 348 |
virtual void sendReinvite(bool updateSDP = true, const string& headers = ""); |
335 | 349 |
/** send an INVITE */ |
336 | 350 |
virtual int sendInvite(const string& headers = ""); |
351 |
+ /** send a PRACK request */ |
|
352 |
+ void sendPrack(const string &sdp_offer, |
|
353 |
+ const string &rseq_val, |
|
354 |
+ const string &cseq_val); |
|
337 | 355 |
|
338 | 356 |
/** set the session on/off hold */ |
339 | 357 |
virtual void setOnHold(bool hold); |
... | ... |
@@ -418,6 +436,14 @@ public: |
418 | 436 |
*/ |
419 | 437 |
virtual void onCancel(){} |
420 | 438 |
|
439 |
+ /** |
|
440 |
+ * onPrack is called when a PRACK request is received for the session. |
|
441 |
+ * The sequencing correctness (RAck fits) is already checked. |
|
442 |
+ * Should be overridden if SDP offer is expected with it. |
|
443 |
+ * @param cnt order of which 1xx this PRACK is for |
|
444 |
+ */ |
|
445 |
+ virtual void onPrack(const AmSipRequest& req, unsigned cnt); |
|
446 |
+ |
|
421 | 447 |
/** |
422 | 448 |
* onSessionStart will be called after call setup. |
423 | 449 |
* |
... | ... |
@@ -464,14 +490,25 @@ public: |
464 | 490 |
virtual void onSipRequest(const AmSipRequest& req); |
465 | 491 |
/** Entry point for SIP Replies */ |
466 | 492 |
virtual void onSipReply(const AmSipReply& reply, int old_dlg_status); |
493 |
+#if 0 |
|
494 |
+ /** There was a timeout receiving a reply for the given SIP request */ |
|
495 |
+ virtual void onSipReqTimeout(const AmSipRequest &req); |
|
496 |
+ /** There was a timeout receiving a request (PR-/ACK) for given SIP reply */ |
|
497 |
+ virtual void onSipRplTimeout(const AmSipRequest &req, const AmSipReply &rpl); |
|
498 |
+#endif |
|
467 | 499 |
|
468 | 500 |
/** 2xx reply has been received for an INVITE transaction */ |
469 | 501 |
virtual void onInvite2xx(const AmSipReply& reply); |
470 | 502 |
|
503 |
+#if 0 |
|
471 | 504 |
/** missing 2xx-ACK */ |
472 | 505 |
virtual void onNo2xxACK(unsigned int cseq); |
473 | 506 |
/** missing non-2xx-ACK */ |
474 | 507 |
virtual void onNoErrorACK(unsigned int cseq); |
508 |
+#else |
|
509 |
+ virtual void onNoAck(unsigned int cseq); |
|
510 |
+ virtual void onNoPrack(const AmSipRequest &req, const AmSipReply &rpl); |
|
511 |
+#endif |
|
475 | 512 |
|
476 | 513 |
/** |
477 | 514 |
* Entry point for Audio events |
... | ... |
@@ -518,6 +555,8 @@ public: |
518 | 555 |
|
519 | 556 |
// The IP address to put as c= in SDP bodies and to use for Contact:. |
520 | 557 |
string advertisedIP(); |
558 |
+ |
|
559 |
+ string sid4dbg(); |
|
521 | 560 |
}; |
522 | 561 |
|
523 | 562 |
inline AmRtpAudio* AmSession::RTPStream() { |
... | ... |
@@ -48,6 +48,17 @@ bool AmSessionEventHandler::onSipReply(const AmSipReply& reply) |
48 | 48 |
return false; |
49 | 49 |
} |
50 | 50 |
|
51 |
+bool AmSessionEventHandler::onSipReqTimeout(const AmSipRequest &) |
|
52 |
+{ |
|
53 |
+ return false; |
|
54 |
+} |
|
55 |
+ |
|
56 |
+bool AmSessionEventHandler::onSipRplTimeout(const AmSipRequest &, |
|
57 |
+ const AmSipReply &) |
|
58 |
+{ |
|
59 |
+ return false; |
|
60 |
+} |
|
61 |
+ |
|
51 | 62 |
bool AmSessionEventHandler::onSendRequest(const string& method, |
52 | 63 |
const string& content_type, |
53 | 64 |
const string& body, |
... | ... |
@@ -67,6 +67,8 @@ public: |
67 | 67 |
|
68 | 68 |
virtual bool onSipRequest(const AmSipRequest&); |
69 | 69 |
virtual bool onSipReply(const AmSipReply&); |
70 |
+ virtual bool onSipReqTimeout(const AmSipRequest &); |
|
71 |
+ virtual bool onSipRplTimeout(const AmSipRequest &, const AmSipReply &); |
|
70 | 72 |
|
71 | 73 |
virtual bool onSendRequest(const string& method, |
72 | 74 |
const string& content_type, |
... | ... |
@@ -254,6 +254,7 @@ void AmSipDialog::updateStatus(const AmSipReply& reply) |
254 | 254 |
break; |
255 | 255 |
|
256 | 256 |
case Pending: |
257 |
+ // TODO [SBGW]: if negative and PRACK, tear down the call (???) |
|
257 | 258 |
case Disconnected: |
258 | 259 |
// only change status of dialog if reply |
259 | 260 |
// to INVITE received |
... | ... |
@@ -299,6 +300,7 @@ void AmSipDialog::uasTimeout(AmSipTimeoutEvent* to_ev) |
299 | 300 |
assert(to_ev); |
300 | 301 |
|
301 | 302 |
switch(to_ev->type){ |
303 |
+#if 0 |
|
302 | 304 |
case AmSipTimeoutEvent::no2xxACK: |
303 | 305 |
DBG("Timeout: missing 2xx-ACK\n"); |
304 | 306 |
if(hdl) hdl->onNo2xxACK(to_ev->cseq); |
... | ... |
@@ -308,10 +310,16 @@ void AmSipDialog::uasTimeout(AmSipTimeoutEvent* to_ev) |
308 | 310 |
DBG("Timeout: missing non-2xx-ACK\n"); |
309 | 311 |
if(hdl) hdl->onNoErrorACK(to_ev->cseq); |
310 | 312 |
break; |
313 |
+#else |
|
314 |
+ case AmSipTimeoutEvent::noACK: |
|
315 |
+ DBG("Timeout: missing ACK\n"); |
|
316 |
+ if(hdl) hdl->onNoAck(to_ev->cseq); |
|
317 |
+ break; |
|
318 |
+#endif |
|
311 | 319 |
|
312 | 320 |
case AmSipTimeoutEvent::noPRACK: |
313 |
- //TODO |
|
314 | 321 |
DBG("Timeout: missing PRACK\n"); |
322 |
+ if(hdl) hdl->onNoPrack(to_ev->req, to_ev->rpl); |
|
315 | 323 |
break; |
316 | 324 |
|
317 | 325 |
case AmSipTimeoutEvent::_noEv: |
... | ... |
@@ -375,8 +383,8 @@ int AmSipDialog::reply(const AmSipRequest& req, |
375 | 383 |
reply.hdrs += SIP_HDR_COLSP(SIP_HDR_SERVER) + AmConfig::Signature + CRLF; |
376 | 384 |
} |
377 | 385 |
|
378 |
- if ((req.method!="CANCEL")&& |
|
379 |
- !((req.method=="BYE")&&(code<300))) |
|
386 |
+ if (code < 300 && req.method != "CANCEL" && req.method != "BYE") |
|
387 |
+ /* if 300<=code<400, explicit contact setting should be done */ |
|
380 | 388 |
reply.contact = getContactHdr(); |
381 | 389 |
|
382 | 390 |
reply.content_type = content_type; |
... | ... |
@@ -489,22 +497,19 @@ int AmSipDialog::invite(const string& hdrs, |
489 | 497 |
} |
490 | 498 |
} |
491 | 499 |
|
492 |
-int AmSipDialog::update(const string& hdrs) |
|
500 |
+int AmSipDialog::update(const string &cont_type, |
|
501 |
+ const string &body, |
|
502 |
+ const string &hdrs) |
|
493 | 503 |
{ |
494 | 504 |
switch(status){ |
495 | 505 |
case Connected: |
496 |
- return sendRequest("UPDATE", "", "", hdrs); |
|
497 |
- case Disconnecting: |
|
498 | 506 |
case Pending: |
499 |
- DBG("update(): we are not yet connected." |
|
500 |
- "(status=%i). do nothing!\n",status); |
|
507 |
+ return sendRequest(SIP_METH_UPDATE, cont_type, body, hdrs); |
|
501 | 508 |
|
502 |
- return 0; |
|
503 | 509 |
default: |
504 |
- DBG("update(): we are not connected " |
|
505 |
- "(status=%i). do nothing!\n",status); |
|
506 |
- return 0; |
|
510 |
+ DBG("update(): dialog not connected (status=%i). do nothing!\n",status); |
|
507 | 511 |
} |
512 |
+ return -1; |
|
508 | 513 |
} |
509 | 514 |
|
510 | 515 |
int AmSipDialog::refer(const string& refer_to, |
... | ... |
@@ -567,6 +572,25 @@ int AmSipDialog::transfer(const string& target) |
567 | 572 |
return 0; |
568 | 573 |
} |
569 | 574 |
|
575 |
+int AmSipDialog::prack(const string &cont_type, |
|
576 |
+ const string &body, |
|
577 |
+ const string &hdrs) |
|
578 |
+{ |
|
579 |
+ switch(status) { |
|
580 |
+ case Pending: |
|
581 |
+ break; |
|
582 |
+ case Disconnected: |
|
583 |
+ case Connected: |
|
584 |
+ case Disconnecting: |
|
585 |
+ ERROR("can not send PRACK while dialog is in state '%d'.\n", status); |
|
586 |
+ return -1; |
|
587 |
+ default: |
|
588 |
+ ERROR("BUG: unexpected dialog state '%d'.\n", status); |
|
589 |
+ return -1; |
|
590 |
+ } |
|
591 |
+ return sendRequest(SIP_METH_PRACK, cont_type, body, hdrs); |
|
592 |
+} |
|
593 |
+ |
|
570 | 594 |
int AmSipDialog::cancel() |
571 | 595 |
{ |
572 | 596 |
for(TransMap::reverse_iterator t = uac_trans.rbegin(); |
... | ... |
@@ -96,11 +96,19 @@ class AmSipDialogEventHandler |
96 | 96 |
/** Hook called when a local INVITE request has been replied with 2xx */ |
97 | 97 |
virtual void onInvite2xx(const AmSipReply& reply)=0; |
98 | 98 |
|
99 |
+#if 0 |
|
99 | 100 |
/** Hook called when a UAS INVITE transaction did not receive a 2xx-ACK */ |
100 | 101 |
virtual void onNo2xxACK(unsigned int cseq)=0; |
101 | 102 |
|
102 | 103 |
/** Hook called when a UAS INVITE transaction did not receive a non-2xx-ACK */ |
103 | 104 |
virtual void onNoErrorACK(unsigned int cseq)=0; |
105 |
+#else |
|
106 |
+ /** Hook called when a UAS INVITE transaction did not receive the ACK */ |
|
107 |
+ virtual void onNoAck(unsigned int)=0; |
|
108 |
+ |
|
109 |
+ /** Hook called when a UAS INVITE transaction did not receive the PRACK */ |
|
110 |
+ virtual void onNoPrack(const AmSipRequest &, const AmSipReply &)=0; |
|
111 |
+#endif |
|
104 | 112 |
|
105 | 113 |
virtual ~AmSipDialogEventHandler() {}; |
106 | 114 |
}; |
... | ... |
@@ -152,6 +160,7 @@ class AmSipDialog |
152 | 160 |
string outbound_proxy; |
153 | 161 |
bool force_outbound_proxy; |
154 | 162 |
|
163 |
+ int rseq; // RSeq for next request (NOTE: keep it signed!) |
|
155 | 164 |
unsigned int cseq; // Local CSeq for next request |
156 | 165 |
bool r_cseq_i; |
157 | 166 |
unsigned int r_cseq; // last remote CSeq |
... | ... |
@@ -193,7 +202,12 @@ class AmSipDialog |
193 | 202 |
|
194 | 203 |
int bye(const string& hdrs = ""); |
195 | 204 |
int cancel(); |
196 |
- int update(const string& hdrs); |
|
205 |
+ int prack(const string &cont_type, |
|
206 |
+ const string &body, |
|
207 |
+ const string &hdrs); |
|
208 |
+ int update(const string &cont_type, |
|
209 |
+ const string &body, |
|
210 |
+ const string &hdrs); |
|
197 | 211 |
int reinvite(const string& hdrs, |
198 | 212 |
const string& content_type, |
199 | 213 |
const string& body); |
... | ... |
@@ -7,6 +7,26 @@ void AmSipTimeoutEvent::operator() (AmSipDialog* dlg) |
7 | 7 |
dlg->uasTimeout(this); |
8 | 8 |
} |
9 | 9 |
|
10 |
+ |
|
11 |
+#if 0 |
|
12 |
+ AmSipReqTimeoutEvent *req_tout_ev = |
|
13 |
+ dynamic_cast<AmSipReqTimeoutEvent *>(sip_ev); |
|
14 |
+ if (req_tout_ev) { |
|
15 |
+ CALL_EVENT_H(onSipReqTimeout, req_tout_ev->req); |
|
16 |
+ onSipReqTimeout(req_tout_ev->req); |
|
17 |
+ return; |
|
18 |
+ } |
|
19 |
+ |
|
20 |
+ AmSipRplTimeoutEvent *rpl_tout_ev = |
|
21 |
+ dynamic_cast<AmSipRplTimeoutEvent *>(sip_ev); |
|
22 |
+ if (rpl_tout_ev) { |
|
23 |
+ CALL_EVENT_H(onSipRplTimeout, rpl_tout_ev->req, rpl_tout_ev->rpl); |
|
24 |
+ onSipRplTimeout(rpl_tout_ev->req, rpl_tout_ev->rpl); |
|
25 |
+ return; |
|
26 |
+ } |
|
27 |
+#endif |
|
28 |
+ |
|
29 |
+ |
|
10 | 30 |
void AmSipRequestEvent::operator() (AmSipDialog* dlg) |
11 | 31 |
{ |
12 | 32 |
assert(dlg); |
... | ... |
@@ -48,6 +48,7 @@ class AmSipEvent: public AmEvent |
48 | 48 |
virtual void operator() (AmSipDialog* dlg)=0; |
49 | 49 |
}; |
50 | 50 |
|
51 |
+#if 0 |
|
51 | 52 |
/** \brief UAS reply re-transmission timeout event */ |
52 | 53 |
class AmSipTimeoutEvent: public AmSipEvent |
53 | 54 |
{ |
... | ... |
@@ -70,6 +71,34 @@ class AmSipTimeoutEvent: public AmSipEvent |
70 | 71 |
|
71 | 72 |
virtual void operator() (AmSipDialog* dlg); |
72 | 73 |
}; |
74 |
+#else |
|
75 |
+/** \brief UAS reply re-transmission timeout event */ |
|
76 |
+class AmSipTimeoutEvent: public AmSipEvent |
|
77 |
+{ |
|
78 |
+ public: |
|
79 |
+ |
|
80 |
+ enum EvType { |
|
81 |
+ _noEv=0, |
|
82 |
+ noACK, |
|
83 |
+ noPRACK, |
|
84 |
+ }; |
|
85 |
+ |
|
86 |
+ EvType type; |
|
87 |
+ |
|
88 |
+ unsigned int cseq; |
|
89 |
+ AmSipRequest req; |
|
90 |
+ AmSipReply rpl; |
|
91 |
+ |
|
92 |
+ AmSipTimeoutEvent(EvType t, unsigned int cseq_num) |
|
93 |
+ : AmSipEvent(), type(t) |
|
94 |
+ {} |
|
95 |
+ AmSipTimeoutEvent(EvType t, AmSipRequest &_req, AmSipReply &_rpl) |
|
96 |
+ : AmSipEvent(), req(_req), rpl(_rpl) |
|
97 |
+ {} |
|
98 |
+ |
|
99 |
+ virtual void operator() (AmSipDialog* dlg); |
|
100 |
+}; |
|
101 |
+#endif |
|
73 | 102 |
|
74 | 103 |
/** \brief SIP request event */ |
75 | 104 |
class AmSipRequestEvent: public AmSipEvent |
... | ... |
@@ -97,4 +126,37 @@ class AmSipReplyEvent: public AmSipEvent |
97 | 126 |
}; |
98 | 127 |
|
99 | 128 |
|
129 |
+#if 0 |
|
130 |
+// TODO: have a single AmSipT.outEv. class, with type and a _AmSipMsgInDlg |
|
131 |
+// ptr: it will save the dynamic_cast efforts later. |
|
132 |
+ |
|
133 |
+class AmSipTimeoutEvent: public AmSipEvent |
|
134 |
+{ |
|
135 |
+ public: |
|
136 |
+ AmSipTimeoutEvent(int id = -1) |
|
137 |
+ : AmSipEvent(-1) |
|
138 |
+ {} |
|
139 |
+}; |
|
140 |
+ |
|
141 |
+class AmSipReqTimeoutEvent : public AmSipTimeoutEvent |
|
142 |
+{ |
|
143 |
+ public: |
|
144 |
+ AmSipRequest req; |
|
145 |
+ AmSipReqTimeoutEvent(AmSipRequest &r): |
|
146 |
+ AmSipTimeoutEvent(), req(r) |
|
147 |
+ {} |
|
148 |
+}; |
|
149 |
+ |
|
150 |
+class AmSipRplTimeoutEvent : public AmSipTimeoutEvent |
|
151 |
+{ |
|
152 |
+ public: |
|
153 |
+ AmSipRequest req; |
|
154 |
+ AmSipReply rpl; |
|
155 |
+ AmSipRplTimeoutEvent(AmSipRequest &_req, AmSipReply &_rpl): |
|
156 |
+ AmSipTimeoutEvent(), req(_req), rpl(_rpl) |
|
157 |
+ {} |
|
158 |
+}; |
|
159 |
+#endif |
|
160 |
+ |
|
161 |
+ |
|
100 | 162 |
#endif |
... | ... |
@@ -4,23 +4,31 @@ |
4 | 4 |
|
5 | 5 |
#define SIP_SCHEME_SIP "sip" |
6 | 6 |
|
7 |
+#define SIP_METH_INVITE "INVITE" |
|
8 |
+#define SIP_METH_PRACK "PRACK" |
|
9 |
+#define SIP_METH_UPDATE "UPDATE" |
|
10 |
+ |
|
7 | 11 |
#define SIP_HDR_FROM "From" |
8 | 12 |
#define SIP_HDR_TO "To" |
9 | 13 |
#define SIP_HDR_ROUTE "Route" |
10 | 14 |
#define SIP_HDR_CONTENT_TYPE "Content-Type" |
11 | 15 |
#define SIP_HDR_CONTACT "Contact" |
12 | 16 |
#define SIP_HDR_SUPPORTED "Supported" |
13 |
-#define SIP_HDR_REQUIRED "Required" |
|
17 |
+#define SIP_HDR_REQUIRE "Require" |
|
14 | 18 |
#define SIP_HDR_SERVER "Server" |
15 | 19 |
#define SIP_HDR_USER_AGENT "User-Agent" |
16 | 20 |
#define SIP_HDR_MAX_FORWARDS "Max-Forwards" |
17 | 21 |
#define SIP_HDR_P_ASSERTED_IDENTITY "P-Asserted-Identity" |
18 | 22 |
#define SIP_HDR_REFER_TO "Refer-To" |
19 | 23 |
#define SIP_HDR_EXPIRES "Expires" |
24 |
+#define SIP_HDR_RSEQ "RSeq" |
|
25 |
+#define SIP_HDR_RACK "RAck" |
|
20 | 26 |
#define SIP_HDR_COL(_hdr) _hdr ":" |
21 | 27 |
#define SIP_HDR_COLSP(_hdr) SIP_HDR_COL(_hdr) " " |
22 | 28 |
|
23 | 29 |
#define CRLF "\r\n" |
24 | 30 |
#define SIP_HDR_LEN(_hdr) (sizeof(_hdr) - /*0-term*/1) |
25 | 31 |
|
32 |
+#define SIP_EXT_100REL "100rel" |
|
33 |
+ |
|
26 | 34 |
#endif /* __AMSIPHEADERS_H__ */ |
... | ... |
@@ -129,7 +129,7 @@ bool removeHeader(string& hdrs, const string& hdr_name) { |
129 | 129 |
buf += string(name) + ":" + "[" + member + "]" + ";"; \ |
130 | 130 |
} while (0) |
131 | 131 |
|
132 |
-string AmSipRequest::print() |
|
132 |
+string AmSipRequest::print() const |
|
133 | 133 |
{ |
134 | 134 |
string buf; |
135 | 135 |
|
... | ... |
@@ -155,7 +155,7 @@ string AmSipRequest::print() |
155 | 155 |
return buf; |
156 | 156 |
} |
157 | 157 |
|
158 |
-string AmSipReply::print() |
|
158 |
+string AmSipReply::print() const |
|
159 | 159 |
{ |
160 | 160 |
string buf; |
161 | 161 |
|
... | ... |
@@ -19,15 +19,16 @@ class _AmSipMsgInDlg |
19 | 19 |
string hdrs; |
20 | 20 |
string body; |
21 | 21 |
unsigned int cseq; |
22 |
+ unsigned int rseq; |
|
22 | 23 |
string callid; |
23 | 24 |
|
24 | 25 |
// transaction ticket from sip stack |
25 | 26 |
trans_ticket tt; |
26 | 27 |
|
27 |
- _AmSipMsgInDlg() : cseq(0) { } |
|
28 |
+ _AmSipMsgInDlg() : cseq(0), rseq(0) { } |
|
28 | 29 |
virtual ~_AmSipMsgInDlg() { }; |
29 | 30 |
|
30 |
- virtual string print() = 0; |
|
31 |
+ virtual string print() const = 0; |
|
31 | 32 |
}; |
32 | 33 |
|
33 | 34 |
/** \brief represents a SIP reply */ |
... | ... |
@@ -45,7 +46,7 @@ class AmSipReply : public _AmSipMsgInDlg |
45 | 46 |
|
46 | 47 |
AmSipReply() : code(0), _AmSipMsgInDlg() { } |
47 | 48 |
~AmSipReply() { } |
48 |
- string print(); |
|
49 |
+ string print() const; |
|
49 | 50 |
}; |
50 | 51 |
|
51 | 52 |
|
... | ... |
@@ -67,7 +68,7 @@ class AmSipRequest : public _AmSipMsgInDlg |
67 | 68 |
AmSipRequest() : _AmSipMsgInDlg() { } |
68 | 69 |
~AmSipRequest() { } |
69 | 70 |
|
70 |
- string print(); |
|
71 |
+ string print() const; |
|
71 | 72 |
}; |
72 | 73 |
|
73 | 74 |
string getHeader(const string& hdrs,const string& hdr_name); |
... | ... |
@@ -28,12 +28,15 @@ |
28 | 28 |
|
29 | 29 |
#include "AmUtils.h" |
30 | 30 |
#include "AmSipMsg.h" |
31 |
+#include "AmSipHeaders.h" |
|
31 | 32 |
|
32 | 33 |
#include "sip/trans_layer.h" |
33 | 34 |
#include "sip/sip_parser.h" |
34 | 35 |
#include "sip/parse_header.h" |
35 | 36 |
#include "sip/parse_from_to.h" |
36 | 37 |
#include "sip/parse_cseq.h" |
38 |
+#include "sip/parse_extensions.h" |
|
39 |
+#include "sip/parse_100rel.h" |
|
37 | 40 |
#include "sip/hash_table.h" |
38 | 41 |
#include "sip/sip_trans.h" |
39 | 42 |
#include "sip/wheeltimer.h" |
... | ... |
@@ -300,7 +303,50 @@ int SipCtrlInterface::send(const AmSipReply &rep) |
300 | 303 |
} |
301 | 304 |
} |
302 | 305 |
|
303 |
- |
|
306 |
+ |
|
307 |
+ /* check if we need to store RSeq. */ |
|
308 |
+ // FIXME: shouldn't the global "100rel==on?" check be present here?? |
|
309 |
+ if(100 < rep.code && rep.code < 200 && rep.method == SIP_METH_INVITE) { |
|
310 |
+ unsigned ext = 0; |
|
311 |
+ unsigned rseq = 0; |
|
312 |
+ for(list<sip_header*>::iterator it = msg.hdrs.begin(); |
|
313 |
+ !(ext && rseq) && it != msg.hdrs.end(); ++it) { |
|
314 |
+ // check if there's a Require field containing 100rel; if there |
|
315 |
+ // is, look for RSeq and store it's value in transaction; |
|
316 |
+ DBG("HT:%d, ext:%d, rseq:%d.\n", (*it)->type, ext, rseq); |
|
317 |
+ switch ((*it)->type) { |
|
318 |
+ case sip_header::H_REQUIRE: |
|
319 |
+ if (ext) |
|
320 |
+ // there was alrady a(nother?) Require HF |
|
321 |
+ continue; |
|
322 |
+ if(!parse_extensions(&ext, (*it)->value.s, (*it)->value.len)) { |
|
323 |
+ ERROR("failed to parse(own?)'" SIP_HDR_REQUIRE "' hdr.\n"); |
|
324 |
+ continue; |
|
325 |
+ } |
|
326 |
+ if (rseq) { // our RSeq's are never 0 |
|
327 |
+ rep.tt._t->last_rseq = rseq; |
|
328 |
+ continue; // the end. |
|
329 |
+ } |
|
330 |
+ break; |
|
331 |
+ |
|
332 |
+ case sip_header::H_RSEQ: |
|
333 |
+ if (rseq) { |
|
334 |
+ ERROR("multiple '" SIP_HDR_RSEQ "' headers in reply.\n"); |
|
335 |
+ continue; |
|
336 |
+ } |
|
337 |
+ if (! parse_rseq(&rseq, (*it)->value.s, (*it)->value.len)) { |
|
338 |
+ ERROR("failed to parse (own?) '" SIP_HDR_RSEQ "' hdr.\n"); |
|
339 |
+ continue; |
|
340 |
+ } |
|
341 |
+ if (ext) { |
|
342 |
+ rep.tt._t->last_rseq = rseq; |
|
343 |
+ continue; // the end. |
|
344 |
+ } |
|
345 |
+ } |
|
346 |
+ } |
|
347 |
+ } |
|
348 |
+ |
|
349 |
+ |
|
304 | 350 |
unsigned int hdrs_len = copy_hdrs_len(msg.hdrs); |
305 | 351 |
|
306 | 352 |
if(!rep.body.empty()) { |
... | ... |
@@ -331,17 +377,10 @@ int SipCtrlInterface::send(const AmSipReply &rep) |
331 | 377 |
return ret; |
332 | 378 |
} |
333 | 379 |
|
334 |
-#define DBG_PARAM(p)\ |
|
335 |
- DBG("%s = <%s>\n",#p,p.c_str()); |
|
336 | 380 |
|
337 |
-void SipCtrlInterface::handle_sip_request(const trans_ticket& tt, sip_msg* msg) |
|
381 |
+inline void SipCtrlInterface::sip_msg2am_request(const sip_msg *msg, |
|
382 |
+ AmSipRequest &req) |
|
338 | 383 |
{ |
339 |
- assert(msg); |
|
340 |
- assert(msg->from && msg->from->p); |
|
341 |
- assert(msg->to && msg->to->p); |
|
342 |
- |
|
343 |
- AmSipRequest req; |
|
344 |
- |
|
345 | 384 |
req.cmd = "sems"; |
346 | 385 |
req.method = c2stlstr(msg->u.request->method_str); |
347 | 386 |
req.user = c2stlstr(msg->u.request->ruri.user); |
... | ... |
@@ -370,7 +409,7 @@ void SipCtrlInterface::handle_sip_request(const trans_ticket& tt, sip_msg* msg) |
370 | 409 |
} |
371 | 410 |
} |
372 | 411 |
else { |
373 |
- if (req.method == "INVITE") { |
|
412 |
+ if (req.method == SIP_METH_INVITE) { |
|
374 | 413 |
WARN("Request has no contact header\n"); |
375 | 414 |
WARN("\trequest = '%.*s'\n",msg->len,msg->buf); |
376 | 415 |
} |
... | ... |
@@ -393,50 +432,26 @@ void SipCtrlInterface::handle_sip_request(const trans_ticket& tt, sip_msg* msg) |
393 | 432 |
req.cseq = get_cseq(msg)->num; |
394 | 433 |
req.body = c2stlstr(msg->body); |
395 | 434 |
|
396 |
- req.tt = tt; |
|
435 |
+ if (msg->rack) |
|
436 |
+ req.rseq = get_rack(msg)->rseq; |
|
397 | 437 |
|
398 | 438 |
if (msg->content_type) |
399 | 439 |
req.content_type = c2stlstr(msg->content_type->value); |
400 | 440 |
|
401 | 441 |
prepare_routes_uas(msg->record_route, req.route); |
402 | 442 |
|
403 |
- for (list<sip_header*>::iterator it = msg->hdrs.begin(); |
|
443 |
+ for (list<sip_header *>::const_iterator it = msg->hdrs.begin(); |
|
404 | 444 |
it != msg->hdrs.end(); ++it) { |
405 |
- if((*it)->type == sip_header::H_OTHER){ |
|
445 |
+ if((*it)->type == sip_header::H_OTHER || |
|
446 |
+ (*it)->type == sip_header::H_REQUIRE){ |
|
406 | 447 |
req.hdrs += c2stlstr((*it)->name) + ": " |
407 |
- + c2stlstr((*it)->value) + "\r\n"; |
|
448 |
+ + c2stlstr((*it)->value) + CRLF; |
|
408 | 449 |
} |
409 | 450 |
} |
410 |
- |
|
411 |
- DBG("Received new request\n"); |
|
412 |
- if (SipCtrlInterface::log_parsed_messages) { |
|
413 |
- // DBG_PARAM(req.cmd); |
|
414 |
- DBG_PARAM(req.method); |
|
415 |
- // DBG_PARAM(req.user); |
|
416 |
- // DBG_PARAM(req.domain); |
|
417 |
- DBG_PARAM(req.r_uri); |
|
418 |
- DBG_PARAM(req.from_uri); |
|
419 |
- DBG_PARAM(req.from); |
|
420 |
- DBG_PARAM(req.to); |
|
421 |
- DBG_PARAM(req.callid); |
|
422 |
- DBG_PARAM(req.from_tag); |
|
423 |
- DBG_PARAM(req.to_tag); |
|
424 |
- DBG("cseq = <%i>\n",req.cseq); |
|
425 |
- DBG_PARAM(req.route); |
|
426 |
- DBG("hdrs = <%s>\n",req.hdrs.c_str()); |
|
427 |
- DBG("body = <%s>\n",req.body.c_str()); |
|
428 |
- } |
|
429 |
- |
|
430 |
- AmSipDispatcher::instance()->handleSipMsg(req); |
|
431 | 451 |
} |
432 | 452 |
|
433 |
-void SipCtrlInterface::handle_sip_reply(sip_msg* msg) |
|
453 |
+inline bool SipCtrlInterface::sip_msg2am_reply(sip_msg *msg, AmSipReply &reply) |
|
434 | 454 |
{ |
435 |
- assert(msg->from && msg->from->p); |
|
436 |
- assert(msg->to && msg->to->p); |
|
437 |
- |
|
438 |
- AmSipReply reply; |
|
439 |
- |
|
440 | 455 |
reply.content_type = msg->content_type ? c2stlstr(msg->content_type->value): ""; |
441 | 456 |
|
442 | 457 |
reply.body = msg->body.len ? c2stlstr(msg->body) : ""; |
... | ... |
@@ -456,7 +471,7 @@ void SipCtrlInterface::handle_sip_reply(sip_msg* msg) |
456 | 471 |
if(err < 0) { |
457 | 472 |
|
458 | 473 |
ERROR("Contact nameaddr parsing failed\n"); |
459 |
- return; |
|
474 |
+ return false; |
|
460 | 475 |
} |
461 | 476 |
|
462 | 477 |
// 'Contact' header? |
... | ... |
@@ -474,15 +489,78 @@ void SipCtrlInterface::handle_sip_reply(sip_msg* msg) |
474 | 489 |
reply.remote_tag = c2stlstr(((sip_from_to*)msg->to->p)->tag); |
475 | 490 |
reply.local_tag = c2stlstr(((sip_from_to*)msg->from->p)->tag); |
476 | 491 |
|
492 |
+ |
|
477 | 493 |
prepare_routes_uac(msg->record_route, reply.route); |
478 | 494 |
|
495 |
+ unsigned rseq; |
|
479 | 496 |
for (list<sip_header*>::iterator it = msg->hdrs.begin(); |
480 | 497 |
it != msg->hdrs.end(); ++it) { |
481 |
- if((*it)->type == sip_header::H_OTHER){ |
|
482 |
- reply.hdrs += c2stlstr((*it)->name) + ": " |
|
483 |
- + c2stlstr((*it)->value) + "\r\n"; |
|
484 |
- } |
|
498 |
+ switch ((*it)->type) { |
|
499 |
+ case sip_header::H_OTHER: |
|
500 |
+ case sip_header::H_REQUIRE: |
|
501 |
+ reply.hdrs += c2stlstr((*it)->name) + ": " |
|
502 |
+ + c2stlstr((*it)->value) + CRLF; |
|
503 |
+ break; |
|
504 |
+ case sip_header::H_RSEQ: |
|
505 |
+ if (! parse_rseq(&rseq, (*it)->value.s, (*it)->value.len)) { |
|
506 |
+ ERROR("failed to parse (rcvd) '" SIP_HDR_RSEQ "' hdr.\n"); |
|
507 |
+ } else { |
|
508 |
+ reply.rseq = rseq; |
|
509 |
+ } |
|
510 |
+ break; |
|
511 |
+ } |
|
485 | 512 |
} |
513 |
+ |
|
514 |
+ return true; |
|
515 |
+} |
|
516 |
+ |
|
517 |
+ |
|
518 |
+#define DBG_PARAM(p)\ |
|
519 |
+ DBG("%s = <%s>\n",#p,p.c_str()); |
|
520 |
+ |
|
521 |
+void SipCtrlInterface::handle_sip_request(const trans_ticket& tt, sip_msg* msg) |
|
522 |
+{ |
|
523 |
+ assert(msg); |
|
524 |
+ assert(msg->from && msg->from->p); |
|
525 |
+ assert(msg->to && msg->to->p); |
|
526 |
+ |
|
527 |
+ AmSipRequest req; |
|
528 |
+ |
|
529 |
+ sip_msg2am_request(msg, req); |
|
530 |
+ |
|
531 |
+ req.tt = tt; |
|
532 |
+ |
|
533 |
+ DBG("Received new request\n"); |
|
534 |
+ if (SipCtrlInterface::log_parsed_messages) { |
|
535 |
+ // DBG_PARAM(req.cmd); |
|
536 |
+ DBG_PARAM(req.method); |
|
537 |
+ // DBG_PARAM(req.user); |
|
538 |
+ // DBG_PARAM(req.domain); |
|
539 |
+ DBG_PARAM(req.r_uri); |
|
540 |
+ DBG_PARAM(req.from_uri); |
|
541 |
+ DBG_PARAM(req.from); |
|
542 |
+ DBG_PARAM(req.to); |
|
543 |
+ DBG_PARAM(req.callid); |
|
544 |
+ DBG_PARAM(req.from_tag); |
|
545 |
+ DBG_PARAM(req.to_tag); |
|
546 |
+ DBG("cseq = <%i>\n",req.cseq); |
|
547 |
+ DBG_PARAM(req.route); |
|
548 |
+ DBG("hdrs = <%s>\n",req.hdrs.c_str()); |
|
549 |
+ DBG("body = <%s>\n",req.body.c_str()); |
|
550 |
+ } |
|
551 |
+ |
|
552 |
+ AmSipDispatcher::instance()->handleSipMsg(req); |
|
553 |
+} |
|
554 |
+ |
|
555 |
+void SipCtrlInterface::handle_sip_reply(sip_msg* msg) |
|
556 |
+{ |
|
557 |
+ assert(msg->from && msg->from->p); |
|
558 |
+ assert(msg->to && msg->to->p); |
|
559 |
+ |
|
560 |
+ AmSipReply reply; |
|
561 |
+ |
|
562 |
+ if (! sip_msg2am_reply(msg, reply)) |
|
563 |
+ return; |
|
486 | 564 |
|
487 | 565 |
DBG("Received reply: %i %s\n",reply.code,reply.reason.c_str()); |
488 | 566 |
DBG_PARAM(reply.callid); |
... | ... |
@@ -493,8 +571,7 @@ void SipCtrlInterface::handle_sip_reply(sip_msg* msg) |
493 | 571 |
AmSipDispatcher::instance()->handleSipMsg(reply); |
494 | 572 |
} |
495 | 573 |
|
496 |
-#undef DBG_PARAM |
|
497 |
- |
|
574 |
+#if 0 |
|
498 | 575 |
void SipCtrlInterface::timer_expired(sip_trans* trans, sip_timer_type tt) |
499 | 576 |
{ |
500 | 577 |
assert(trans); |
... | ... |
@@ -542,6 +619,66 @@ void SipCtrlInterface::timer_expired(sip_trans* trans, sip_timer_type tt) |
542 | 619 |
AmEventDispatcher::instance()->post(c2stlstr(trans->to_tag), |
543 | 620 |
new AmSipTimeoutEvent(ev, cseq->num)); |
544 | 621 |
} |
622 |
+#endif |
|
623 |
+ |
|
624 |
+ |
|
625 |
+void SipCtrlInterface::handle_reply_timeout(AmSipTimeoutEvent::EvType evt, |
|
626 |
+ sip_trans *tr, trans_bucket *buk) |
|
627 |
+{ |
|
628 |
+ AmSipTimeoutEvent *tmo_evt; |
|
629 |
+ |
|
630 |
+ switch (evt) { |
|
631 |
+ case AmSipTimeoutEvent::noACK: { |
|
632 |
+ sip_cseq* cseq = dynamic_cast<sip_cseq*>(tr->msg->cseq->p); |
|
633 |
+ |
|
634 |
+ if(!cseq){ |
|
635 |
+ ERROR("missing CSeq\n"); |
|
636 |
+ return; |
|
637 |
+ } |
|
638 |
+ tmo_evt = new AmSipTimeoutEvent(evt, cseq->num); |
|
639 |
+ } |
|
640 |
+ break; |
|
641 |
+ |
|
642 |
+ case AmSipTimeoutEvent::noPRACK: { |
|
643 |
+ sip_msg msg(tr->retr_buf, tr->retr_len); |
|
644 |
+ |
|
645 |
+ char* err_msg=0; |
|
646 |
+ int err = parse_sip_msg(&msg, err_msg); |
|
647 |
+ if (err) { |
|
648 |
+ ERROR("failed to parse (own) reply[%d]: %s.\n", err, |
|
649 |
+ err_msg ? err_msg : "???"); |
|
650 |
+ return; |
|
651 |
+ } |
|
652 |
+ |
|
653 |
+ AmSipReply reply; |
|
654 |
+ if (! sip_msg2am_reply(&msg, reply)) { |
|
655 |
+ ERROR("failed to convert sip_msg to AmSipReply.\n"); |
|
656 |
+ return; |
|
657 |
+ } |
|
658 |
+ |
|
659 |
+ AmSipRequest request; |
|
660 |
+ sip_msg2am_request(tr->msg, request); |
|
661 |
+ request.tt = trans_ticket(tr, buk); |
|
662 |
+ |
|
663 |
+ DBG("Reply timed out: %i %s\n",reply.code,reply.reason.c_str()); |
|
664 |
+ DBG_PARAM(reply.callid); |
|
665 |
+ DBG_PARAM(reply.local_tag); |
|
666 |
+ DBG_PARAM(reply.remote_tag); |
|
667 |
+ DBG("cseq = <%i>\n",reply.cseq); |
|
668 |
+ |
|
669 |
+ tmo_evt = new AmSipTimeoutEvent(evt, request, reply); |
|
670 |
+ } |
|
671 |
+ break; |
|
672 |
+ |
|
673 |
+ default: |
|
674 |
+ ERROR("BUG: unexpected timout event type '%d'.\n", evt); |
|
675 |
+ return; |
|
676 |
+ } |
|
677 |
+ |
|
678 |
+ AmEventDispatcher::instance()->post(c2stlstr(tr->to_tag), tmo_evt); |
|
679 |
+} |
|
680 |
+ |
|
681 |
+#undef DBG_PARAM |
|
545 | 682 |
|
546 | 683 |
void SipCtrlInterface::prepare_routes_uac(const list<sip_header*>& routes, string& route_field) |
547 | 684 |
{ |
... | ... |
@@ -49,6 +49,9 @@ class udp_trsp; |
49 | 49 |
class SipCtrlInterface: |
50 | 50 |
public sip_ua |
51 | 51 |
{ |
52 |
+ void sip_msg2am_request(const sip_msg *msg, AmSipRequest &request); |
|
53 |
+ bool sip_msg2am_reply(sip_msg *msg, AmSipReply &reply); |
|
54 |
+ |
|
52 | 55 |
void prepare_routes_uac(const list<sip_header*>& routes, string& route_field); |
53 | 56 |
void prepare_routes_uas(const list<sip_header*>& routes, string& route_field); |
54 | 57 |
|
... | ... |
@@ -104,7 +107,11 @@ public: |
104 | 107 |
*/ |
105 | 108 |
void handle_sip_request(const trans_ticket& tt, sip_msg* msg); |
106 | 109 |
void handle_sip_reply(sip_msg* msg); |
110 |
+ void handle_reply_timeout(AmSipTimeoutEvent::EvType evt, |
|
111 |
+ sip_trans *tr, trans_bucket *buk=0); |
|
112 |
+#if 0 |
|
107 | 113 |
void timer_expired(sip_trans* trans, sip_timer_type tt); |
114 |
+#endif |
|
108 | 115 |
}; |
109 | 116 |
|
110 | 117 |
|
... | ... |
@@ -119,7 +119,7 @@ bool SessionTimer::onSendReply(const AmSipRequest& req, |
119 | 119 |
|
120 | 120 |
if (((session_refresher_role==UAC) && (session_refresher==refresh_remote)) |
121 | 121 |
|| ((session_refresher_role==UAS) && remote_timer_aware)) |
122 |
- m_hdrs += SIP_HDR_COLSP(SIP_HDR_REQUIRED) "timer" CRLF; |
|
122 |
+ m_hdrs += SIP_HDR_COLSP(SIP_HDR_REQUIRE) "timer" CRLF; |
|
123 | 123 |
|
124 | 124 |
hdrs += m_hdrs; |
125 | 125 |
|
... | ... |
@@ -28,11 +28,13 @@ |
28 | 28 |
#include "hash_table.h" |
29 | 29 |
#include "hash.h" |
30 | 30 |
|
31 |
+#include "AmSipHeaders.h" |
|
31 | 32 |
#include "sip_parser.h" |
32 | 33 |
#include "parse_header.h" |
33 | 34 |
#include "parse_cseq.h" |
34 | 35 |
#include "parse_via.h" |
35 | 36 |
#include "parse_from_to.h" |
37 |
+#include "parse_100rel.h" |
|
36 | 38 |
#include "sip_trans.h" |
37 | 39 |
|
38 | 40 |
#include "log.h" |
... | ... |
@@ -112,14 +114,15 @@ sip_trans* trans_bucket::match_request(sip_msg* msg) |
112 | 114 |
} |
113 | 115 |
|
114 | 116 |
if(msg->u.request->method != (*it)->msg->u.request->method) { |
115 |
- if( (msg->u.request->method == sip_request::ACK) && |
|
116 |
- ((*it)->msg->u.request->method == sip_request::INVITE) ) { |
|
117 |
- |
|
118 |
- t = match_200_ack(*it,msg); |
|
119 |
- if(t){ |
|
120 |
- break; |
|
121 |
- } |
|
122 |
- } |
|
117 |
+ if((*it)->msg->u.request->method == sip_request::INVITE) { |
|
118 |
+ if(msg->u.request->method == sip_request::ACK) { |
|
119 |
+ if ((t = match_200_ack(*it,msg))) |
|
120 |
+ break; |
|
121 |
+ } else if(msg->u.request->method == sip_request::PRACK) { |
|
122 |
+ if ((t = match_1xx_prack(*it,msg))) |
|
123 |
+ break; |
|
124 |
+ } |
|
125 |
+ } |
|
123 | 126 |
|
124 | 127 |
continue; |
125 | 128 |
} |
... | ... |
@@ -348,6 +351,50 @@ sip_trans* trans_bucket::match_200_ack(sip_trans* t, sip_msg* msg) |
348 | 351 |
return t; |
349 | 352 |
} |
350 | 353 |
|
354 |
+sip_trans* trans_bucket::match_1xx_prack(sip_trans* t, sip_msg* msg) |
|
355 |
+{ |
|
356 |
+ /* first, check quickly if lenghts match (From tag, To tag, Call-ID) */ |
|
357 |
+ |
|
358 |
+ sip_from_to* from = dynamic_cast<sip_from_to*>(msg->from->p); |
|
359 |
+ sip_from_to* t_from = dynamic_cast<sip_from_to*>(t->msg->from->p); |
|
360 |
+ if(from->tag.len != t_from->tag.len) |
|
361 |
+ return 0; |
|
362 |
+ |
|
363 |
+ sip_from_to* to = dynamic_cast<sip_from_to*>(msg->to->p); |
|
364 |
+ if(to->tag.len != t->to_tag.len) |
|
365 |
+ return 0; |
|
366 |
+ |
|
367 |
+ if(msg->callid->value.len != t->msg->callid->value.len) |
|
368 |
+ return 0; |
|
369 |
+ |
|
370 |
+ |
|
371 |
+ sip_rack *rack = dynamic_cast<sip_rack *>(msg->rack->p); |
|
372 |
+ sip_cseq* t_cseq = dynamic_cast<sip_cseq*>(t->msg->cseq->p); |
|
373 |
+ if (rack->cseq != t_cseq->num) |
|
374 |
+ return 0; |
|
375 |
+ |
|
376 |
+ if (rack->rseq != t->last_rseq) |
|
377 |
+ return 0; |
|
378 |
+ |
|
379 |
+ if (rack->method != t->msg->u.request->method) |
|
380 |
+ return 0; |
|
381 |
+ |
|
382 |
+ |
|
383 |
+ /* numbers fit, try content */ |
|
384 |
+ |
|
385 |
+ if(memcmp(from->tag.s,t_from->tag.s,from->tag.len)) |
|
386 |
+ return NULL; |
|
387 |
+ |
|
388 |
+ if(memcmp(msg->callid->value.s,t->msg->callid->value.s, |
|
389 |
+ msg->callid->value.len)) |
|
390 |
+ return NULL; |
|
391 |
+ |
|
392 |
+ if(memcmp(to->tag.s,t->to_tag.s,to->tag.len)) |
|
393 |
+ return NULL; |
|
394 |
+ |
|
395 |
+ return t; |
|
396 |
+} |
|
397 |
+ |
|
351 | 398 |
sip_trans* trans_bucket::add_trans(sip_msg* msg, int ttype) |
352 | 399 |
{ |
353 | 400 |
sip_trans* t = new sip_trans(); |
72 | 73 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,122 @@ |
1 |
+ |
|
2 |
+#include <limits.h> |
|
3 |
+ |
|
4 |
+#include "log.h" |
|
5 |
+#include "AmSipHeaders.h" |
|
6 |
+#include "sip_parser.h" |
|
7 |
+#include "parse_common.h" |
|
8 |
+#include "parse_100rel.h" |
|
9 |
+ |
|
10 |
+ |
|
11 |
+#define EAT_WS(_c_, _end_) \ |
|
12 |
+ do { \ |
|
13 |
+ while ((_c_) < (_end_) && (*(_c_) == ' ' || *(_c_) == '\t')) \ |
|
14 |
+ (_c_) ++; \ |
|
15 |
+ } while (0) |
|
16 |
+ |
|
17 |
+#define READ_NUMBER(_no, _c_, _end_) \ |
|
18 |
+ do { \ |
|
19 |
+ register bool fin; \ |
|
20 |
+ (_no) = 0; \ |
|
21 |
+ for (fin = false; !fin && (_c_) < (_end_); ) { \ |
|
22 |
+ switch (*(_c_)) { \ |
|
23 |
+ case '0' ... '9': \ |
|
24 |
+ if (UINT_MAX - (_no) < (unsigned)*(_c_)) { \ |
|
25 |
+ INFO("not an uint32_t.\n"); \ |
|
26 |
+ goto error; \ |
|
27 |
+ } \ |
|
28 |
+ (_no) = (_no) * 10 + *(_c_) - '0'; \ |
|
29 |
+ break; \ |
|
30 |
+ default: \ |
|
31 |
+ fin = true; \ |
|
32 |
+ continue; \ |
|
33 |
+ } \ |
|
34 |
+ (_c_) ++; \ |
|
35 |
+ } \ |
|
36 |
+ } while (0) |
|
37 |
+ |
|
38 |
+#define EAT_TOKEN(_c_, _end_) \ |
|
39 |
+ do { \ |
|
40 |
+ while ((_c_) < (_end_) && IS_TOKEN(*(_c_))) \ |
|
41 |
+ (_c_) ++; \ |
|
42 |
+ } while (0) |
|
43 |
+ |
|
44 |
+ |
|
45 |
+bool parse_rseq(unsigned *_rseq, const char *start, int len) |
|
46 |
+{ |
|
47 |
+ unsigned rseq; |
|
48 |
+ const char *pos = start; |
|
49 |
+ const char *end = start + len; |
|
50 |
+ const char *sav; |
|
51 |
+ |
|
52 |
+ EAT_WS(pos, end); |
|
53 |
+ |
|
54 |
+ sav = pos; |
|
55 |
+ READ_NUMBER(rseq, pos, end); |
|
56 |
+ if (sav == pos) |
|
57 |
+ goto error; |
|
58 |
+ |
|
59 |
+ *_rseq = rseq; |
|
60 |
+ DBG("parsed sequence content: %u.\n", rseq); |
|
61 |
+ return true; |
|
62 |
+ |
|
63 |
+error: |
|
64 |
+ INFO("invalid content in sequence header content <%.*s>.\n", len, start); |
|
65 |
+ return false; |
|
66 |
+} |
|
67 |
+ |
|
68 |
+ |
|
69 |
+bool parse_rack(sip_rack *rack, const char *start, int len) |
|
70 |
+{ |
|
71 |
+ const char *pos; |
|
72 |
+ const char *sav; |
|
73 |
+ const char *end = start + len; |
|
74 |
+ unsigned rseq, cseq; |
|
75 |
+ cstring method_str; |
|
76 |
+ cstring cseq_str; |
|
77 |
+ int method; |
|
78 |
+ |
|
79 |
+ pos = start; |
|
80 |
+ |
|
81 |
+ EAT_WS(pos, end); |
|
82 |
+ |
|
83 |
+ sav = pos; |
|
84 |
+ READ_NUMBER(rseq, pos, end); |
|
85 |
+ if (pos == sav) |
|
86 |
+ goto error; |
|
87 |
+ |
|
88 |
+ EAT_WS(pos, end); |
|
89 |
+ |
|
90 |
+ sav = pos; |
|
91 |
+ READ_NUMBER(cseq, pos, end); |
|
92 |
+ if (pos == sav) |
|
93 |
+ goto error; |
|
94 |
+ cseq_str.s = sav; |
|
95 |
+ cseq_str.len = pos - sav; |
|
96 |
+ |
|
97 |
+ EAT_WS(pos, end); |
|
98 |
+ |
|
99 |
+ sav = pos; |
|
100 |
+ EAT_TOKEN(pos, end); |
|
101 |
+ method_str.s = sav; |
|
102 |
+ method_str.len = pos - sav; |
|
103 |
+ |
|