diff --git a/core/plug-in/sipctrl/SipCtrlInterface.cpp b/core/plug-in/sipctrl/SipCtrlInterface.cpp index 0d24b39f..17fd39ca 100644 --- a/core/plug-in/sipctrl/SipCtrlInterface.cpp +++ b/core/plug-in/sipctrl/SipCtrlInterface.cpp @@ -63,6 +63,20 @@ SipCtrlInterface::SipCtrlInterface(const string& bind_addr, unsigned short bind_ tl = trans_layer::instance(); } +int SipCtrlInterface::cancel(const AmSipRequest& req) +{ + unsigned int h=0; + unsigned long t=0; + + if((sscanf(req.serKey.c_str(),"%x:%lx",&h,&t) != 2) || + (h >= H_TABLE_ENTRIES)){ + ERROR("Invalid transaction key: invalid bucket ID (key=%s)\n",req.serKey.c_str()); + return -1; + } + + return tl->cancel(get_trans_bucket(h),(sip_trans*)t); +} + #ifndef _STANDALONE string SipCtrlInterface::getContact(const string &displayName, @@ -118,6 +132,9 @@ string SipCtrlInterface::getContact(const string &displayName, int SipCtrlInterface::send(const AmSipRequest &req, string &serKey) { + if(req.method == "CANCEL") + return cancel(req); + sip_msg* msg = new sip_msg(); msg->type = SIP_REQUEST; @@ -223,10 +240,13 @@ int SipCtrlInterface::send(const AmSipRequest &req, string &serKey) msg->body = stl2cstr(req.body); } } - - tl->send_request(msg); + + char tid[12]; + + tl->send_request(msg,tid); delete msg; + serKey = string(tid,12); return 0; } diff --git a/core/plug-in/sipctrl/SipCtrlInterface.h b/core/plug-in/sipctrl/SipCtrlInterface.h index b46ed14a..772ecf6b 100644 --- a/core/plug-in/sipctrl/SipCtrlInterface.h +++ b/core/plug-in/sipctrl/SipCtrlInterface.h @@ -61,6 +61,7 @@ class SipCtrlInterface: void prepare_routes(const list& routes, string& route_field); + int cancel(const AmSipRequest& req); protected: void run(); diff --git a/core/plug-in/sipctrl/trans_layer.cpp b/core/plug-in/sipctrl/trans_layer.cpp index 1748bf2b..5d8e6bcf 100644 --- a/core/plug-in/sipctrl/trans_layer.cpp +++ b/core/plug-in/sipctrl/trans_layer.cpp @@ -188,10 +188,6 @@ int trans_layer::send_reply(trans_bucket* bucket, sip_trans* t, } } - //if(add_contact){ - //contact_wr(&c,contact); - //} - if (hdrs.len) { memcpy(c,hdrs.s,hdrs.len); c += hdrs.len; @@ -297,13 +293,12 @@ int trans_layer::set_next_hop(list& route_hdrs, if(!is_lr){ - // TODO: detect beginning of next route - // + // detect beginning of next route enum { RR_PARAMS=0, RR_QUOTED, - RR_SEP_SWS, // space(s) after ',' + RR_SEP_SWS, // space(s) after ',' RR_NXT_ROUTE }; @@ -374,11 +369,10 @@ int trans_layer::set_next_hop(list& route_hdrs, } - // TODO: - copy r_uri at the end of - // the route set. - // + // copy r_uri at the end of + // the route set. route_hdrs.push_back(new sip_header(0,"Route",r_uri)); - DBG("route_hdrs.push_back(0x%p)\n",route_hdrs.back()); + r_uri = na.addr; } @@ -408,7 +402,7 @@ int trans_layer::set_next_hop(list& route_hdrs, return 0; } -int trans_layer::send_request(sip_msg* msg) +int trans_layer::send_request(sip_msg* msg, char* tid) { // Request-URI // To @@ -423,7 +417,8 @@ int trans_layer::send_request(sip_msg* msg) assert(transport); - if(set_next_hop(msg->route,msg->u.request->ruri_str,&msg->remote_ip) < 0){ + if(set_next_hop(msg->route,msg->u.request->ruri_str, + &msg->remote_ip) < 0){ // TODO: error handling DBG("set_next_hop failed\n"); //delete msg; @@ -516,20 +511,150 @@ int trans_layer::send_request(sip_msg* msg) } else { - // TODO: which timer is suitable? - // if transport == UDP t->reset_timer(STIMER_E,E_TIMER,bucket->get_id()); // for any transport type t->reset_timer(STIMER_F,F_TIMER,bucket->get_id()); } + + string t_id = int2hex(bucket->get_id()).substr(5,string::npos) + + ":" + long2hex((unsigned long)t); + memcpy(tid,t_id.c_str(),12); + + bucket->unlock(); + } + + return send_err; +} + +int trans_layer::cancel(trans_bucket* bucket, sip_trans* t) +{ + bucket->lock(); + if(!bucket->exist(t)){ + DBG("No transaction to cancel: wrong key or finally replied\n"); + bucket->unlock(); + return 0; + } + + sip_msg* req = t->msg; + + // RFC 3261 says: SHOULD NOT be sent for other request + // than INVITE. + if(req->u.request->method != sip_request::INVITE){ + bucket->unlock(); + ERROR("Trying to cancel a non-INVITE request (we SHOULD NOT do that)\n"); + return -1; + } + + switch(t->state){ + case TS_CALLING: + // do not send a request: + // just remove the transaction + bucket->remove_trans(t); bucket->unlock(); + return 0; + + case TS_COMPLETED: + // final reply has been sent: + // do nothing!!! + bucket->unlock(); + return 0; + + case TS_PROCEEDING: + // continue with CANCEL request + break; + } + + cstring cancel_str("CANCEL"); + + int request_len = request_line_len(cancel_str, + req->u.request->ruri_str); + + char branch_buf[BRANCH_BUF_LEN]; + cstring branch(branch_buf,BRANCH_BUF_LEN); + compute_branch(branch.s,req->callid->value,get_cseq(req)->num_str); + + string via(transport->get_local_ip()); + if(transport->get_local_port() != 5060) + via += ":" + int2str(transport->get_local_port()); + + //request_len += via_len(stl2cstr(via),branch); + request_len += copy_hdr_len(req->via1); + + request_len += copy_hdr_len(req->to) + + copy_hdr_len(req->from) + + copy_hdr_len(req->callid) + + cseq_len(get_cseq(req)->num_str,cancel_str) + + copy_hdrs_len(req->route) + + copy_hdr_len(req->contact); + + request_len += 2/* CRLF end-of-headers*/; + + // Allocate new message + sip_msg* p_msg = new sip_msg(); + p_msg->buf = new char[request_len]; + p_msg->len = request_len; + + // generate it + char* c = p_msg->buf; + request_line_wr(&c,cancel_str, + req->u.request->ruri_str); + + copy_hdr_wr(&c,req->via1); + copy_hdr_wr(&c,req->to); + copy_hdr_wr(&c,req->from); + copy_hdr_wr(&c,req->callid); + cseq_wr(&c,get_cseq(req)->num_str,cancel_str); + copy_hdrs_wr(&c,req->route); + copy_hdr_wr(&c,req->contact); + + *c++ = CR; + *c++ = LF; + + // and parse it + if(parse_sip_msg(p_msg)){ + ERROR("Parser failed on generated request\n"); + ERROR("Message was: <%.*s>\n",p_msg->len,p_msg->buf); + delete p_msg; + return MALFORMED_SIP_MSG; + } + + memcpy(&p_msg->remote_ip,&req->remote_ip,sizeof(sockaddr_storage)); + + DBG("Sending to %s:%i <%.*s>\n", + get_addr_str(((sockaddr_in*)&p_msg->remote_ip)->sin_addr).c_str(), + ntohs(((sockaddr_in*)&p_msg->remote_ip)->sin_port), + p_msg->len,p_msg->buf); + + int send_err = transport->send(&p_msg->remote_ip,p_msg->buf,p_msg->len); + if(send_err < 0){ + ERROR("Error from transport layer\n"); + delete p_msg; + } + else { + trans_bucket* n_bucket = get_trans_bucket(p_msg->callid->value, + get_cseq(p_msg)->num_str); + + if(bucket != n_bucket) + n_bucket->lock(); + + sip_trans* t = n_bucket->add_trans(p_msg,TT_UAC); + + // if transport == UDP + t->reset_timer(STIMER_E,E_TIMER,bucket->get_id()); + // for any transport type + t->reset_timer(STIMER_F,F_TIMER,bucket->get_id()); + + if(bucket != n_bucket) + n_bucket->unlock(); } + bucket->unlock(); return send_err; } + void trans_layer::received_msg(sip_msg* msg) { #define DROP_MSG \ @@ -689,7 +814,7 @@ int trans_layer::update_uac_trans(trans_bucket* bucket, sip_trans* t, sip_msg* m } to_tag = ((sip_from_to*)msg->to->p)->tag; - if(!to_tag.len){ + if((t->msg->u.request->method != sip_request::CANCEL) && !to_tag.len){ DBG("To-tag missing in final reply\n"); return -1; } @@ -754,7 +879,10 @@ int trans_layer::update_uac_trans(trans_bucket* bucket, sip_trans* t, sip_msg* m // TODO: timer should be 0 if reliable transport t->reset_timer(STIMER_K, K_TIMER, bucket->get_id()); - goto pass_reply; + if(t->msg->u.request->method != sip_request::CANCEL) + goto pass_reply; + else + goto end; case TS_COMPLETED: // Absorb reply retransmission (only if UDP) diff --git a/core/plug-in/sipctrl/trans_layer.h b/core/plug-in/sipctrl/trans_layer.h index bbaadcc3..2e0a02ee 100644 --- a/core/plug-in/sipctrl/trans_layer.h +++ b/core/plug-in/sipctrl/trans_layer.h @@ -109,8 +109,18 @@ class trans_layer * Caution: Route headers should not be added to the * general header list (msg->hdrs). * @param msg Pre-built message. + * @param tid buffer for the transaction key (char[12]) */ - int send_request(sip_msg* msg); + int send_request(sip_msg* msg, char* tid); + + /** + * Cancels a request. + * A CANCEL request is sent if necessary. + * @param bucket bucket of the original request. + * @param t transaction of the original request. + */ + int cancel(trans_bucket* bucket, sip_trans* t); + /** * Called by the transport layer