diff --git a/include/re_sipsess.h b/include/re_sipsess.h index 87006197b..4aff7da41 100644 --- a/include/re_sipsess.h +++ b/include/re_sipsess.h @@ -7,6 +7,14 @@ struct sipsess_sock; struct sipsess; +/* SDP Negotiation state */ +enum sdp_neg_state { + SDP_NEG_NONE = 0, + SDP_NEG_LOCAL_OFFER, /** SDP offer sent */ + SDP_NEG_REMOTE_OFFER, /** SDP offer received */ + SDP_NEG_DONE /** SDP negotiation done */ +}; + typedef void (sipsess_conn_h)(const struct sip_msg *msg, void *arg); typedef int (sipsess_desc_h)(struct mbuf **descp, const struct sa *src, diff --git a/src/sipsess/accept.c b/src/sipsess/accept.c index ab50daeff..1f506dc7e 100644 --- a/src/sipsess/accept.c +++ b/src/sipsess/accept.c @@ -99,6 +99,9 @@ int sipsess_accept(struct sipsess **sessp, struct sipsess_sock *sock, if (err) goto out; + if (mbuf_get_left(msg->mb)) + sess->neg_state = SDP_NEG_REMOTE_OFFER; + va_start(ap, fmt); if (scode > 100 && scode < 200) { diff --git a/src/sipsess/connect.c b/src/sipsess/connect.c index 1f56218ce..685cc495c 100644 --- a/src/sipsess/connect.c +++ b/src/sipsess/connect.c @@ -68,7 +68,9 @@ static int send_handler(enum sip_transp tp, struct sa *src, *contp = cont; out: - sess->sent_offer = desc != NULL; + if (desc) + sess->neg_state = SDP_NEG_LOCAL_OFFER; + mem_deref(desc); return err; } @@ -89,12 +91,13 @@ static void invite_resp_handler(int err, const struct sip_msg *msg, void *arg) if (!msg || err || sip_request_loops(&sess->ls, msg->scode)) goto out; + sdp = mbuf_get_left(msg->mb) > 0; + if (msg->scode < 200) { sess->progrh(msg, sess->arg); - sdp = mbuf_get_left(msg->mb) > 0; - if (sdp && sess->sent_offer) { - sess->awaiting_answer = false; + if (sdp && sess->neg_state == SDP_NEG_LOCAL_OFFER) { + sess->neg_state = SDP_NEG_DONE; err = sess->answerh(msg, sess->arg); if (err) goto out; @@ -110,8 +113,8 @@ static void invite_resp_handler(int err, const struct sip_msg *msg, void *arg) if (sip_msg_hdr_has_value(msg, SIP_HDR_REQUIRE, "100rel") && sess->rel100_supported) { - if (sdp && !sess->sent_offer) { - sess->modify_pending = false; + if (sdp && sess->neg_state == SDP_NEG_NONE) { + sess->neg_state = SDP_NEG_REMOTE_OFFER; err = sess->offerh(&desc, msg, sess->arg); } @@ -135,15 +138,27 @@ static void invite_resp_handler(int err, const struct sip_msg *msg, void *arg) if (err) goto out; - if (sess->sent_offer) - err = sess->answerh(msg, sess->arg); - else { - sess->modify_pending = false; - err = sess->offerh(&desc, msg, sess->arg); + if (sdp) { + if (sess->neg_state == SDP_NEG_LOCAL_OFFER) { + sess->neg_state = SDP_NEG_DONE; + err = sess->answerh(msg, sess->arg); + } else if (sess->neg_state == SDP_NEG_NONE) { + sess->neg_state = SDP_NEG_REMOTE_OFFER; + err = sess->offerh(&desc, msg, sess->arg); + } + + if (err) + goto out; } - err |= sipsess_ack(sess->sock, sess->dlg, msg->cseq.num, - sess->auth, sess->ctype, desc); + err = sipsess_ack(sess->sock, sess->dlg, msg->cseq.num, + sess->auth, sess->ctype, desc); + if (err) + goto out; + + if (sess->neg_state == SDP_NEG_REMOTE_OFFER + && mbuf_get_left(desc)) + sess->neg_state = SDP_NEG_DONE; sess->established = true; mem_deref(desc); diff --git a/src/sipsess/listen.c b/src/sipsess/listen.c index a47fda866..896409eaa 100644 --- a/src/sipsess/listen.c +++ b/src/sipsess/listen.c @@ -147,7 +147,7 @@ static void ack_handler(struct sipsess_sock *sock, const struct sip_msg *msg) } if (awaiting_answer) { - sess->awaiting_answer = false; + sess->neg_state = SDP_NEG_DONE; err = sess->answerh(msg, sess->arg); } @@ -193,18 +193,18 @@ static void prack_handler(struct sipsess_sock *sock, const struct sip_msg *msg) } if (awaiting_prack) { - sess->awaiting_prack = false; - sess->refresh_allowed = true; + --sess->prack_waiting_cnt; } if (sess->prackh) sess->prackh(msg, sess->arg); if (awaiting_answer) { - sess->awaiting_answer = false; + sess->neg_state = SDP_NEG_DONE; (void)sess->answerh(msg, sess->arg); } else if (msg && mbuf_get_left(msg->mb)) { + sess->neg_state = SDP_NEG_REMOTE_OFFER; (void)sess->offerh(&desc, msg, sess->arg); } @@ -239,7 +239,8 @@ static void target_refresh_handler(struct sipsess_sock *sock, return; } - if ((is_invite && sess->st) || sess->awaiting_answer) { + if ((is_invite && sess->st) + || sess->neg_state == SDP_NEG_LOCAL_OFFER) { (void)sip_treplyf(NULL, NULL, sip, msg, false, 500, "Server Internal Error", "Retry-After: 5\r\n" diff --git a/src/sipsess/modify.c b/src/sipsess/modify.c index 3f7872be8..b9d7c715b 100644 --- a/src/sipsess/modify.c +++ b/src/sipsess/modify.c @@ -32,10 +32,13 @@ static void reinvite_resp_handler(int err, const struct sip_msg *msg, struct sipsess *sess = arg; const struct sip_hdr *hdr; struct mbuf *desc = NULL; + bool sdp; if (!msg || err || sip_request_loops(&sess->ls, msg->scode)) goto out; + sdp = mbuf_get_left(msg->mb) > 0; + if (msg->scode < 200) { return; } @@ -43,20 +46,33 @@ static void reinvite_resp_handler(int err, const struct sip_msg *msg, (void)sip_dialog_update(sess->dlg, msg); - if (sess->sent_offer) { - (void)sess->answerh(msg, sess->arg); - } - else { - sess->modify_pending = false; - (void)sess->offerh(&desc, msg, sess->arg); + if (sdp) { + if (sess->neg_state == SDP_NEG_LOCAL_OFFER) { + sess->neg_state = SDP_NEG_DONE; + err = sess->answerh(msg, sess->arg); + } else if (sess->neg_state == SDP_NEG_NONE) { + sess->neg_state = SDP_NEG_REMOTE_OFFER; + err = sess->offerh(&desc, msg, sess->arg); + } + + if (err) + goto out; } - (void)sipsess_ack(sess->sock, sess->dlg, msg->cseq.num, + err = sipsess_ack(sess->sock, sess->dlg, msg->cseq.num, sess->auth, sess->ctype, desc); + if (err) + goto out; + + if (sess->neg_state == SDP_NEG_REMOTE_OFFER + && mbuf_get_left(desc)) + sess->neg_state = SDP_NEG_DONE; mem_deref(desc); } else { + sess->neg_state = SDP_NEG_DONE; + if (sess->terminated) goto out; @@ -126,28 +142,35 @@ static int send_handler(enum sip_transp tp, struct sa *src, int sipsess_reinvite(struct sipsess *sess, bool reset_ls) { + int err; + if (sess->req) return EPROTO; - sess->sent_offer = sess->desc ? true : false; - sess->modify_pending = false; - if (reset_ls) sip_loopstate_reset(&sess->ls); - return sip_drequestf(&sess->req, sess->sip, true, "INVITE", - sess->dlg, 0, sess->auth, - send_handler, reinvite_resp_handler, sess, - "%s%s%s" - "Content-Length: %zu\r\n" - "\r\n" - "%b", - sess->desc ? "Content-Type: " : "", - sess->desc ? sess->ctype : "", - sess->desc ? "\r\n" : "", - sess->desc ? mbuf_get_left(sess->desc) :(size_t)0, - sess->desc ? mbuf_buf(sess->desc) : NULL, - sess->desc ? mbuf_get_left(sess->desc):(size_t)0); + err = sip_drequestf(&sess->req, sess->sip, true, "INVITE", + sess->dlg, 0, sess->auth, + send_handler, reinvite_resp_handler, sess, + "%s%s%s" + "Content-Length: %zu\r\n" + "\r\n" + "%b", + sess->desc ? "Content-Type: " : "", + sess->desc ? sess->ctype : "", + sess->desc ? "\r\n" : "", + sess->desc ? mbuf_get_left(sess->desc) :(size_t)0, + sess->desc ? mbuf_buf(sess->desc) : NULL, + sess->desc ? mbuf_get_left(sess->desc):(size_t)0); + + if (!err) { + sess->modify_pending = false; + if (sess->desc) + sess->neg_state = SDP_NEG_LOCAL_OFFER; + } + + return err; } @@ -161,12 +184,11 @@ int sipsess_reinvite(struct sipsess *sess, bool reset_ls) */ int sipsess_modify(struct sipsess *sess, struct mbuf *desc) { - if (!sess || sess->terminated || sess->awaiting_answer - || !sip_dialog_established(sess->dlg)) + if (!sess || sess->terminated || !sip_dialog_established(sess->dlg)) return EINVAL; - if (!sess->established && !sess->refresh_allowed - && mbuf_get_left(desc)) + if (mbuf_get_left(desc) && (sess->neg_state != SDP_NEG_DONE + && sess->neg_state != SDP_NEG_NONE)) return EPROTO; mem_deref(sess->desc); diff --git a/src/sipsess/prack.c b/src/sipsess/prack.c index cb23d6f8f..81a59712e 100644 --- a/src/sipsess/prack.c +++ b/src/sipsess/prack.c @@ -68,9 +68,8 @@ static void prack_resp_handler(int err, const struct sip_msg *msg, void *arg) (void)sip_dialog_update(req->sess->dlg, msg); if (mbuf_get_left(msg->mb)) { - if (req->sess->sent_offer) { - req->sess->awaiting_answer = false; - req->sess->refresh_allowed = true; + if (req->sess->neg_state == SDP_NEG_LOCAL_OFFER) { + req->sess->neg_state = SDP_NEG_DONE; (void)req->sess->answerh(msg, req->sess->arg); } @@ -136,10 +135,6 @@ static int prack_request(struct sipsess_prack *prack) if (err == -1) return err; - if (req->sess->sent_offer && !req->sess->awaiting_answer - && (!req->body || !mbuf_get_left(req->body))) - req->sess->refresh_allowed = true; - return sip_drequestf(&req->req, req->sess->sip, true, "PRACK", req->sess->dlg, 0, req->sess->auth, NULL, prack_resp_handler, prack, diff --git a/src/sipsess/reply.c b/src/sipsess/reply.c index efb89089b..af779d80e 100644 --- a/src/sipsess/reply.c +++ b/src/sipsess/reply.c @@ -115,11 +115,12 @@ int sipsess_reply_2xx(struct sipsess *sess, const struct sip_msg *msg, struct sipsess_reply *reply = NULL; struct sip_contact contact; int err = ENOMEM; + bool sdp = mbuf_get_left(msg->mb) > 0; bool non_invite = !pl_strcmp(&msg->met, "PRACK") || !pl_strcmp(&msg->met, "UPDATE"); if (!non_invite) { - if (sess->awaiting_prack) + if (sess->prack_waiting_cnt > 0) return EINVAL; reply = mem_zalloc(sizeof(*reply), destructor); @@ -160,14 +161,18 @@ int sipsess_reply_2xx(struct sipsess *sess, const struct sip_msg *msg, (void)list_ledata(list_apply(&sess->replyl, false, cancel_1xx_timers, NULL)); + if (desc) { + if (sdp) { + sess->neg_state = SDP_NEG_DONE; + } else { + reply->awaiting_answer = true; + sess->neg_state = SDP_NEG_LOCAL_OFFER; + } + } + if (reply) { tmr_start(&reply->tmr, 64 * SIP_T1, tmr_handler, reply); tmr_start(&reply->tmrg, SIP_T1, retransmit_handler, reply); - - if (!mbuf_get_left(msg->mb) && desc) { - reply->awaiting_answer = true; - sess->awaiting_answer = true; - } } out: @@ -269,16 +274,14 @@ int sipsess_reply_1xx(struct sipsess *sess, const struct sip_msg *msg, mem_deref(reply); } - if (desc) { - if (!mbuf_get_left(msg->mb)) { - reply->awaiting_answer = true; - sess->awaiting_answer = true; - } - if (reliably) { - sess->awaiting_prack = true; - reply->awaiting_prack = true; + if (reliably) { + ++sess->prack_waiting_cnt; + reply->awaiting_prack = true; + if (desc) { + reply->awaiting_answer = !mbuf_get_left(msg->mb); + sess->neg_state = mbuf_get_left(msg->mb) ? + SDP_NEG_DONE : SDP_NEG_LOCAL_OFFER; } - } out: diff --git a/src/sipsess/sess.c b/src/sipsess/sess.c index bb62db33c..63985e5eb 100644 --- a/src/sipsess/sess.c +++ b/src/sipsess/sess.c @@ -333,7 +333,7 @@ void sipsess_abort(struct sipsess *sess) */ bool sipsess_awaiting_prack(const struct sipsess *sess) { - return sess ? sess->awaiting_prack : false; + return sess ? sess->prack_waiting_cnt > 0 : false; } @@ -349,8 +349,7 @@ bool sipsess_refresh_allowed(const struct sipsess *sess) if (!sess) return false; - return ((sess->established || sess->refresh_allowed) - && !sess->terminated && !sess->awaiting_answer); + return !sess->terminated && sess->neg_state == SDP_NEG_DONE; } @@ -366,3 +365,36 @@ bool sipsess_ack_pending(const struct sipsess *sess) { return sess && sess->replyl.head ? true : false; } + + +/** + * Get the SDP negotiation state of a SIP Session + * + * @param sess SIP Session + * + * @return SDP negotiation state + */ +enum sdp_neg_state sipsess_sdp_neg_state(const struct sipsess *sess) +{ + return sess ? sess->neg_state : SDP_NEG_NONE; +} + + +/** + * Set the SDP negotiation state of a SIP Session + * + * @param sess SIP Session + * @param state SDP negotiation state + * + * @return 0 on success, otherwise errorcode + */ +int sipsess_set_sdp_neg_state(struct sipsess *sess, + enum sdp_neg_state state) +{ + if (!sess) + return EINVAL; + + sess->neg_state = state; + + return 0; +} diff --git a/src/sipsess/sipsess.h b/src/sipsess/sipsess.h index 884d03705..fbcdc392a 100644 --- a/src/sipsess/sipsess.h +++ b/src/sipsess/sipsess.h @@ -36,15 +36,13 @@ struct sipsess { void *arg; uint32_t rel_seq; bool owner; - bool sent_offer; - bool awaiting_answer; bool modify_pending; bool established; bool peerterm; bool rel100_supported; - bool awaiting_prack; - bool refresh_allowed; + int prack_waiting_cnt; int terminated; + enum sdp_neg_state neg_state; }; @@ -107,3 +105,6 @@ int sipsess_request_alloc(struct sipsess_request **reqp, struct sipsess *sess, sip_resp_h *resph, void *arg); struct sipsess *sipsess_find(struct sipsess_sock *sock, const struct sip_msg *msg); +enum sdp_neg_state sipsess_sdp_neg_state(const struct sipsess *sess); +int sipsess_set_sdp_neg_state(struct sipsess *sess, + enum sdp_neg_state state); diff --git a/src/sipsess/update.c b/src/sipsess/update.c index fbbf25eea..1e982f858 100644 --- a/src/sipsess/update.c +++ b/src/sipsess/update.c @@ -46,15 +46,17 @@ static void update_resp_handler(int err, const struct sip_msg *msg, void *arg) else if (msg->scode < 300) { (void)sip_dialog_update(req->sess->dlg, msg); - if (req->sess->sent_offer) { + if (req->sess->neg_state == SDP_NEG_LOCAL_OFFER) { (void)req->sess->answerh(msg, req->sess->arg); - req->sess->awaiting_answer = false; + req->sess->neg_state = SDP_NEG_DONE; } } else { if (req->sess->terminated) goto out; + req->sess->neg_state = SDP_NEG_DONE; + switch (msg->scode) { case 401: @@ -120,10 +122,12 @@ static int send_handler(enum sip_transp tp, struct sa *src, static int update_request(struct sipsess_request *req) { + int err; + if (!req || req->tmr.th) return -1; - return sip_drequestf(&req->req, req->sess->sip, true, "UPDATE", + err = sip_drequestf(&req->req, req->sess->sip, true, "UPDATE", req->sess->dlg, 0, req->sess->auth, send_handler, update_resp_handler, req, "%s%s%s" @@ -136,6 +140,11 @@ static int update_request(struct sipsess_request *req) req->body ? mbuf_get_left(req->body) :(size_t)0, req->body ? mbuf_buf(req->body) : NULL, req->body ? mbuf_get_left(req->body):(size_t)0); + + if (!err && req->sess->desc) + req->sess->neg_state = SDP_NEG_LOCAL_OFFER; + + return err; } @@ -165,8 +174,6 @@ int sipsess_update(struct sipsess *sess) return err; } - sess->sent_offer = sess->desc ? true : false; - sess->awaiting_answer = sess->sent_offer; sess->modify_pending = false; return err; diff --git a/test/sipsess.c b/test/sipsess.c index c0051fb0b..d49998088 100644 --- a/test/sipsess.c +++ b/test/sipsess.c @@ -16,7 +16,7 @@ typedef void (prack_func)(void *arg); -enum sdp_neg_state { +enum neg_state { INITIAL = 0, OFFER_RECEIVED, ANSWER_RECEIVED, @@ -56,7 +56,7 @@ struct test { bool offer_b; enum rel100_mode rel100_a; enum rel100_mode rel100_b; - enum sdp_neg_state sdp_state; + enum neg_state sdp_state; enum rel100_state rel100_state_a; enum rel100_state rel100_state_b; enum connect_action conn_action; @@ -454,7 +454,7 @@ static void conn_handler(const struct sip_msg *msg, void *arg) else if (test->conn_action & CONN_ANSWER) { err = sipsess_accept(&test->b, test->sock, msg, 200, "OK", test->rel100_b, "b", "application/sdp", - NULL, NULL, NULL, false, offer_handler_b, + desc, NULL, NULL, false, offer_handler_b, answer_handler_b, estab_handler_b, NULL, NULL, close_handler, test, hdrs); if (err != test->answ_ret_code) { @@ -556,7 +556,7 @@ int test_sipsess(void) err = sipsess_connect(&test.a, test.sock, to_uri, NULL, "sip:a@127.0.0.1", "a", NULL, 0, "application/sdp", NULL, NULL, false, - callid, desc_handler, + callid, desc_handler_a, offer_handler_a, answer_handler_a, NULL, estab_handler_a, NULL, NULL, close_handler, &test, NULL); @@ -578,7 +578,9 @@ int test_sipsess(void) ASSERT_TRUE(test.estab_b); ASSERT_TRUE(test.desc); ASSERT_TRUE(test.answr_a); - ASSERT_TRUE(!test.offer_b); + ASSERT_TRUE(test.offer_b); + ASSERT_TRUE(!test.offer_a); + ASSERT_TRUE(!test.answr_b); out: test.a = mem_deref(test.a);