diff --git a/include/re_sipsess.h b/include/re_sipsess.h index 5405c3d51..d59c285ef 100644 --- a/include/re_sipsess.h +++ b/include/re_sipsess.h @@ -68,6 +68,8 @@ int sipsess_modify(struct sipsess *sess, struct mbuf *desc); int sipsess_info(struct sipsess *sess, const char *ctype, struct mbuf *body, sip_resp_h *resph, void *arg); int sipsess_set_close_headers(struct sipsess *sess, const char *hdrs, ...); +bool sipsess_awaiting_prack(struct sipsess *sess); +bool sipsess_refresh_allowed(struct sipsess *sess); void sipsess_close_all(struct sipsess_sock *sock); struct sip_dialog *sipsess_dialog(const struct sipsess *sess); void sipsess_abort(struct sipsess *sess); diff --git a/src/sipsess/listen.c b/src/sipsess/listen.c index c4b41ccd9..6aafa76a3 100644 --- a/src/sipsess/listen.c +++ b/src/sipsess/listen.c @@ -28,8 +28,6 @@ static void destructor(void *arg) mem_deref(sock->ht_sess); hash_flush(sock->ht_ack); mem_deref(sock->ht_ack); - hash_flush(sock->ht_prack); - mem_deref(sock->ht_prack); } @@ -174,10 +172,12 @@ static void prack_handler(struct sipsess_sock *sock, const struct sip_msg *msg) struct sipsess *sess; struct mbuf *desc = NULL; bool awaiting_answer = false; + bool awaiting_prack = false; sess = sipsess_find(sock, msg); - if (!sess || sipsess_reply_ack(sess, msg, &awaiting_answer)) { + if (!sess || sipsess_reply_prack(sess, msg, &awaiting_answer, + &awaiting_prack)) { (void)sip_reply(sock->sip, msg, 481, "Transaction Does Not Exist"); return; @@ -192,6 +192,11 @@ static void prack_handler(struct sipsess_sock *sock, const struct sip_msg *msg) return; } + if (awaiting_prack) { + sess->awaiting_prack = false; + sess->refresh_allowed = true; + } + if (sess->prackh) sess->prackh(msg, sess->arg); @@ -379,10 +384,6 @@ int sipsess_listen(struct sipsess_sock **sockp, struct sip *sip, if (err) goto out; - err = hash_alloc(&sock->ht_prack, htsize); - if (err) - goto out; - sock->sip = sip; sock->connh = connh ? connh : internal_connect_handler; sock->arg = connh ? arg : sock; diff --git a/src/sipsess/modify.c b/src/sipsess/modify.c index 0b1da1578..7d0208400 100644 --- a/src/sipsess/modify.c +++ b/src/sipsess/modify.c @@ -161,7 +161,8 @@ int sipsess_reinvite(struct sipsess *sess, bool reset_ls) */ int sipsess_modify(struct sipsess *sess, struct mbuf *desc) { - if (!sess || (sess->st && sess->established) || sess->terminated) + if (!sess || (!sess->established && !sess->refresh_allowed) + || sess->terminated || sess->awaiting_answer) return EINVAL; mem_deref(sess->desc); diff --git a/src/sipsess/prack.c b/src/sipsess/prack.c index 6f3b4abc4..cb23d6f8f 100644 --- a/src/sipsess/prack.c +++ b/src/sipsess/prack.c @@ -19,78 +19,147 @@ struct sipsess_prack { - struct le he; - struct tmr tmr; - struct sa dst; - struct sip_request *req; - struct sip_dialog *dlg; - struct sipsess_sock *sock; - struct mbuf *mb; - enum sip_transp tp; uint32_t cseq; + uint32_t rseq; + char *met; + struct sipsess_request *req; }; +static int prack_request(struct sipsess_prack *prack); + + static void destructor(void *arg) { struct sipsess_prack *prack = arg; - hash_unlink(&prack->he); - tmr_cancel(&prack->tmr); + mem_deref(prack->met); mem_deref(prack->req); - mem_deref(prack->dlg); - mem_deref(prack->sock); - mem_deref(prack->mb); } static void tmr_handler(void *arg) { struct sipsess_prack *prack = arg; + int err; - mem_deref(prack); -} - - -static int send_handler(enum sip_transp tp, struct sa *src, - const struct sa *dst, struct mbuf *mb, - struct mbuf **contp, void *arg) -{ - struct sipsess_prack *prack = arg; - (void)src; - (void)contp; - - mem_deref(prack->mb); - prack->mb = mem_ref(mb); - prack->dst = *dst; - prack->tp = tp; + if (!prack) + return; - tmr_start(&prack->tmr, 64 * SIP_T1, tmr_handler, prack); - return 0; + err = prack_request(prack); + if (err) + mem_deref(prack); } -static void resp_handler(int err, const struct sip_msg *msg, void *arg) +static void prack_resp_handler(int err, const struct sip_msg *msg, void *arg) { - struct sipsess *sess; struct sipsess_prack *prack = arg; - if (err || !msg) - goto out; + struct sipsess_request *req = prack->req; + const struct sip_hdr *hdr; - sess = sipsess_find(prack->sock, msg); - if (!sess || sess->terminated) + if (!msg || err || sip_request_loops(&req->ls, msg->scode)) goto out; - if (sess->prackh) - sess->prackh(msg, sess->arg); + if (msg->scode < 200) { + return; + } + else if (msg->scode < 300) { + (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; + (void)req->sess->answerh(msg, req->sess->arg); + } + + req->sess->desc = mem_deref(req->sess->desc); + } + } + else { + if (req->sess->terminated) + goto out; + + switch (msg->scode) { + + case 401: + case 407: + err = sip_auth_authenticate(req->sess->auth, msg); + if (err) { + err = (err == EAUTH) ? 0 : err; + break; + } + + err = prack_request(prack); + if (err) + break; + + return; + case 408: + case 491: + tmr_start(&req->tmr, req->sess->owner ? 3000 : 1000, + tmr_handler, req); + return; + case 500: + hdr = sip_msg_hdr(msg, SIP_HDR_RETRY_AFTER); + if (!hdr) + break; + + tmr_start(&req->tmr, pl_u32(&hdr->val) * 1000, + tmr_handler, req); + return; + } + } out: + if (!req->sess->terminated) { + if (err == ETIMEDOUT) + sipsess_terminate(req->sess, err, NULL); + } + mem_deref(prack); } +static int prack_request(struct sipsess_prack *prack) +{ + struct sipsess_request *req = prack->req; + char rack_header[256]; + int err; + + if (!req || req->tmr.th) + return EINVAL; + + err = re_snprintf(rack_header, sizeof(rack_header), "%d %d %s", + prack->rseq, prack->cseq, prack->met); + 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, + "RAck: %s\n" + "%s%s%s" + "Content-Length: %zu\r\n" + "\r\n" + "%b", + rack_header, + req->body ? "Content-Type: " : "", + req->body ? req->sess->ctype : "", + req->body ? "\r\n" : "", + 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); +} + + /** - * Send PRACK message (RFC 3262) + * Send PRACK request (RFC 3262) * * @param sess SIP Session * @param cseq CSeq number to be written in RAck header @@ -104,42 +173,29 @@ int sipsess_prack(struct sipsess *sess, uint32_t cseq, uint32_t rseq, const struct pl *met, struct mbuf *desc) { struct sipsess_prack *prack; - char rack_header[256]; - char method[64]; int err; + if (!sess || sess->terminated) + return EINVAL; + prack = mem_zalloc(sizeof(*prack), destructor); if (!prack) return ENOMEM; - hash_append(sess->sock->ht_prack, - hash_joaat_str(sip_dialog_callid(sess->dlg)), - &prack->he, prack); + err = sipsess_request_alloc(&prack->req, sess, sess->ctype, desc, + NULL, prack); + if (err) + goto out; - prack->dlg = mem_ref(sess->dlg); - prack->sock = mem_ref(sess->sock); prack->cseq = cseq; + prack->rseq = rseq; + err = pl_strdup(&prack->met, met); + if (err) + goto out; - (void)pl_strcpy(met, method, sizeof(method)); - re_snprintf(rack_header, sizeof(rack_header), "%d %d %s", rseq, cseq, - method); - - err = sip_drequestf(&prack->req, sess->sock->sip, true, "PRACK", - sess->dlg, cseq, sess->auth, send_handler, - resp_handler, prack, - "RAck: %s\n" - "%s%s%s" - "Content-Length: %zu\r\n" - "\r\n" - "%b", - rack_header, - desc ? "Content-Type: " : "", - desc ? sess->ctype : "", - desc ? "\r\n" : "", - desc ? mbuf_get_left(desc) : (size_t)0, - desc ? mbuf_buf(desc) : NULL, - desc ? mbuf_get_left(desc) : (size_t)0); + err = prack_request(prack); +out: if (err) mem_deref(prack); diff --git a/src/sipsess/reply.c b/src/sipsess/reply.c index fe7f26a78..cbb11dd73 100644 --- a/src/sipsess/reply.c +++ b/src/sipsess/reply.c @@ -27,6 +27,8 @@ struct sipsess_reply { struct mbuf *mb; struct sipsess *sess; bool awaiting_answer; + bool awaiting_prack; + uint16_t scode; uint32_t seq; uint32_t rel_seq; uint32_t txc; @@ -50,19 +52,28 @@ static void tmr_handler(void *arg) struct sipsess_reply *reply = arg; struct sipsess *sess = reply->sess; - mem_deref(reply); - /* wait for all pending ACKs */ if (sess->replyl.head) - return; + goto out; /* we want to send bye */ - sess->established = true; - if (!sess->terminated) - sipsess_terminate(sess, ETIMEDOUT, NULL); - else + if (!sess->terminated) { + if (reply->scode < 200 && !sess->established) { + (void)sip_reply(sess->sip, reply->msg, 504, + "Timeout"); + } + else { + sess->established = true; + sipsess_terminate(sess, ETIMEDOUT, NULL); + } + } + else { mem_deref(sess); + } + +out: + mem_deref(reply); } @@ -83,6 +94,29 @@ static void retransmit_handler(void *arg) } +static bool cancel_1xx_timers(struct le *le, void *arg) +{ + struct sipsess_reply *reply = le->data; + (void)arg; + + if (reply->scode > 100 && reply->scode < 200) { + tmr_cancel(&reply->tmr); + tmr_cancel(&reply->tmrg); + } + + return false; +} + + +static bool is_1xx_reply(struct le *le, void *arg) +{ + struct sipsess_reply *reply = le->data; + (void)arg; + + return reply->scode > 100 && reply->scode < 200; +} + + int sipsess_reply_2xx(struct sipsess *sess, const struct sip_msg *msg, uint16_t scode, const char *reason, struct mbuf *desc, const char *fmt, va_list *ap) @@ -94,6 +128,9 @@ int sipsess_reply_2xx(struct sipsess *sess, const struct sip_msg *msg, || !pl_strcmp(&msg->met, "UPDATE"); if (!non_invite) { + if (sess->awaiting_prack) + return EINVAL; + reply = mem_zalloc(sizeof(*reply), destructor); if (!reply) goto out; @@ -103,6 +140,7 @@ int sipsess_reply_2xx(struct sipsess *sess, const struct sip_msg *msg, reply->rel_seq = 0; reply->seq = msg->cseq.num; reply->msg = mem_ref((void *)msg); + reply->scode = scode; reply->sess = sess; } @@ -128,6 +166,10 @@ int sipsess_reply_2xx(struct sipsess *sess, const struct sip_msg *msg, if (err) goto out; + if (!non_invite) + (void)list_ledata(list_apply(&sess->replyl, false, + cancel_1xx_timers, NULL)); + if (reply) { tmr_start(&reply->tmr, 64 * SIP_T1, tmr_handler, reply); tmr_start(&reply->tmrg, SIP_T1, retransmit_handler, reply); @@ -183,7 +225,7 @@ int sipsess_reply_1xx(struct sipsess *sess, const struct sip_msg *msg, return -1; } - reliably = rel100 && rel100_peer; + reliably = rel100 && rel100_peer && scode != 100; if (rel100 != REL100_REQUIRED && reliably) { pl_set_str(&require_header, "Require: 100rel\r\n"); } @@ -192,11 +234,13 @@ int sipsess_reply_1xx(struct sipsess *sess, const struct sip_msg *msg, if (!reply) goto out; - prev = list_ledata(list_tail(&sess->replyl)); + prev = list_ledata(list_apply(&sess->replyl, false, is_1xx_reply, + NULL)); list_append(&sess->replyl, &reply->le, reply); reply->seq = msg->cseq.num; reply->msg = mem_ref((void *)msg); reply->sess = sess; + reply->scode = scode; sip_contact_set(&contact, sess->cuser, &msg->dst, msg->tp); if (reliably) { @@ -237,9 +281,16 @@ int sipsess_reply_1xx(struct sipsess *sess, const struct sip_msg *msg, mem_deref(reply); } - if (!mbuf_get_left(msg->mb) && desc) { - reply->awaiting_answer = true; - sess->awaiting_answer = true; + 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; + } + } out: @@ -252,21 +303,45 @@ int sipsess_reply_1xx(struct sipsess *sess, const struct sip_msg *msg, } -static bool cmp_handler(struct le *le, void *arg) +static bool cmp_handler_prack(struct le *le, void *arg) { struct sipsess_reply *reply = le->data; const struct sip_msg *msg = arg; - if (!pl_strcmp(&msg->met, "PRACK")) { - return msg->rack.cseq == reply->seq && - msg->rack.rel_seq == reply->rel_seq && - !pl_cmp(&msg->rack.met, &reply->msg->met); - } + return msg->rack.cseq == reply->seq && + msg->rack.rel_seq == reply->rel_seq && + !pl_cmp(&msg->rack.met, &reply->msg->met); +} + + +static bool cmp_handler(struct le *le, void *arg) +{ + struct sipsess_reply *reply = le->data; + const struct sip_msg *msg = arg; return msg->cseq.num == reply->seq; } +int sipsess_reply_prack(struct sipsess *sess, const struct sip_msg *msg, + bool *awaiting_answer, bool *awaiting_prack) +{ + struct sipsess_reply *reply; + + reply = list_ledata(list_apply(&sess->replyl, false, cmp_handler_prack, + (void *)msg)); + if (!reply) + return ENOENT; + + *awaiting_answer = reply->awaiting_answer; + *awaiting_prack = reply->awaiting_prack; + + mem_deref(reply); + + return 0; +} + + int sipsess_reply_ack(struct sipsess *sess, const struct sip_msg *msg, bool *awaiting_answer) { diff --git a/src/sipsess/sess.c b/src/sipsess/sess.c index 84491fca3..e919e0d65 100644 --- a/src/sipsess/sess.c +++ b/src/sipsess/sess.c @@ -321,3 +321,34 @@ void sipsess_abort(struct sipsess *sess) sipsess_bye(sess, true); sess->terminated = 2; } + + +/** + * Return true if session is waiting for a PRACK to a 1xx containing SDP + * + * @param sess SIP Session + * + * @return true if session is waiting for a PRACK to a 1xx containing SDP, + * false otherwise + */ +bool sipsess_awaiting_prack(const struct sipsess *sess) +{ + return sess ? sess->awaiting_prack : false; +} + + +/** + * Return true if a target refresh (re-INVITE or UPDATE) is currently allowed + * + * @param sess SIP Session + * + * @return True if a target refresh is currently allowed, otherwise false + */ +bool sipsess_refresh_allowed(const struct sipsess *sess) +{ + if (!sess) + return false; + + return ((sess->established || sess->refresh_allowed) + && !sess->terminated && !sess->awaiting_answer); +} diff --git a/src/sipsess/sipsess.h b/src/sipsess/sipsess.h index 57cc55399..2d740ea46 100644 --- a/src/sipsess/sipsess.h +++ b/src/sipsess/sipsess.h @@ -41,6 +41,8 @@ struct sipsess { bool established; bool peerterm; bool rel100_supported; + bool awaiting_prack; + bool refresh_allowed; int terminated; }; @@ -50,7 +52,6 @@ struct sipsess_sock { struct sip_lsnr *lsnr_req; struct hash *ht_sess; struct hash *ht_ack; - struct hash *ht_prack; struct sip *sip; sipsess_conn_h *connh; void *arg; @@ -95,6 +96,8 @@ int sipsess_reply_1xx(struct sipsess *sess, const struct sip_msg *msg, const char *fmt, va_list *ap); int sipsess_reply_ack(struct sipsess *sess, const struct sip_msg *msg, bool *awaiting_answer); +int sipsess_reply_prack(struct sipsess *sess, const struct sip_msg *msg, + bool *awaiting_answer, bool *awaiting_prack); int sipsess_reinvite(struct sipsess *sess, bool reset_ls); int sipsess_update(struct sipsess *sess); int sipsess_bye(struct sipsess *sess, bool reset_ls); diff --git a/src/sipsess/update.c b/src/sipsess/update.c index f16f2d134..9155f531f 100644 --- a/src/sipsess/update.c +++ b/src/sipsess/update.c @@ -46,8 +46,10 @@ 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->sent_offer) { (void)req->sess->answerh(msg, req->sess->arg); + req->sess->awaiting_answer = false; + } } else { if (req->sess->terminated) @@ -152,8 +154,9 @@ int sipsess_update(struct sipsess *sess) if (!sess || sess->terminated || !sess->ctype || !sess->desc) return EINVAL; - sess->sent_offer = sess->desc ? true : false; - sess->modify_pending = false; + if ((!sess->established && !sess->refresh_allowed) + || sess->awaiting_answer) + return EPROTO; err = sipsess_request_alloc(&req, sess, sess->ctype, sess->desc, NULL, NULL); @@ -161,8 +164,14 @@ int sipsess_update(struct sipsess *sess) return err; err = update_request(req); - if (err) + if (err) { mem_deref(req); + return err; + } + + sess->sent_offer = sess->desc ? true : false; + sess->awaiting_answer = sess->sent_offer; + sess->modify_pending = false; return err; }