bool SessionTimer::onSendReply(const AmSipRequest& req, unsigned int code,const string& reason, const string& content_type,const string& body, string& hdrs, int flags) { //if (!session_timer_conf.getEnableSessionTimer()) // return ""; string m_hdrs = SIP_HDR_COLSP(SIP_HDR_SUPPORTED) "timer" CRLF; if ((req.method != "INVITE") && (req.method != "UPDATE")) return false; // only in 2xx responses to INV/UPD m_hdrs += "Session-Expires: " + int2str(session_interval) + ";refresher="+ (session_refresher_role==UAC ? "uac":"uas")+CRLF; if (((session_refresher_role==UAC) && (session_refresher==refresh_remote)) || ((session_refresher_role==UAS) && remote_timer_aware)) m_hdrs += SIP_HDR_COLSP(SIP_HDR_REQUIRED) "timer" CRLF; hdrs += m_hdrs; return false; }
bool AmSipSubscription::doUnsubscribe() { if (sub_state == SipSubStateTerminated || sub_state == SipSubStateIdle) { DBG("not subscribed - not unsubscribing\n"); return true; } dlg.remote_uri = req.r_uri; string hdrs; hdrs += SIP_HDR_COLSP(SIP_HDR_EVENT) + info.event; if (!info.id.empty()) hdrs += ";id="+info.id; hdrs += CRLF; if (!info.accept.empty()) { hdrs += SIP_HDR_COLSP(SIP_HDR_ACCEPT) + info.accept + CRLF; } hdrs += SIP_HDR_COLSP(SIP_HDR_EXPIRES) "0" CRLF; if (dlg.sendRequest(SIP_METH_SUBSCRIBE, NULL, hdrs) < 0) { WARN("failed to send unsubscription to '%s' (proxy '%s').\n", dlg.remote_uri.c_str(), dlg.outbound_proxy.c_str()); return false; } return true; }
string AmSipSubscription::getSubscribeHdrs() { string hdrs; hdrs += SIP_HDR_COLSP(SIP_HDR_EVENT) + info.event; if (!info.id.empty()) hdrs += ";id="+info.id; hdrs += CRLF; if (!info.accept.empty()) { hdrs += SIP_HDR_COLSP(SIP_HDR_ACCEPT) + info.accept + CRLF; } if (wanted_expires) { hdrs += SIP_HDR_COLSP(SIP_HDR_EXPIRES) + int2str(wanted_expires) + CRLF; } return hdrs; }
/* static */ int AmBasicSipDialog::reply_error(const AmSipRequest& req, unsigned int code, const string& reason, const string& hdrs, msg_logger* logger) { AmSipReply reply; reply.code = code; reply.reason = reason; reply.tt = req.tt; reply.hdrs = hdrs; reply.to_tag = AmSession::getNewId(); if (AmConfig::Signature.length()) reply.hdrs += SIP_HDR_COLSP(SIP_HDR_SERVER) + AmConfig::Signature + CRLF; // add transcoder statistics into reply headers //addTranscoderStats(reply.hdrs); int ret = SipCtrlInterface::send(reply,string(""),logger); if(ret){ ERROR("Could not send reply: code=%i; reason='%s';" " method=%s; call-id=%s; cseq=%i\n", reply.code,reply.reason.c_str(), req.method.c_str(),req.callid.c_str(),req.cseq); } return ret; }
bool AmSIPRegistration::doRegistration() { bool res = true; waiting_result = true; unregistering = false; req.to_tag = ""; req.r_uri = "sip:"+info.domain; dlg.setRemoteTag(string()); dlg.setRemoteUri(req.r_uri); // set outbound proxy as next hop if (!info.proxy.empty()) { dlg.outbound_proxy = info.proxy; } else if (!AmConfig::OutboundProxy.empty()) { dlg.outbound_proxy = AmConfig::OutboundProxy; } string hdrs = SIP_HDR_COLSP(SIP_HDR_EXPIRES) + int2str(expires_interval) + CRLF; int flags=0; if(!info.contact.empty()) { hdrs += SIP_HDR_COLSP(SIP_HDR_CONTACT) "<" + info.contact + ">" + CRLF; flags = SIP_FLAGS_NOCONTACT; } if (dlg.sendRequest(req.method, NULL, hdrs, flags) < 0) { ERROR("failed to send registration.\n"); res = false; waiting_result = false; } // save TS reg_send_begin = time(NULL); return res; }
bool SessionTimer::onSendRequest(const string& method, const string& content_type, const string& body, string& hdrs, int flags, unsigned int cseq) { string m_hdrs = SIP_HDR_COLSP(SIP_HDR_SUPPORTED) "timer" CRLF; if ((method != "INVITE") && (method != "UPDATE")) goto end; m_hdrs += "Session-Expires: "+ int2str(session_timer_conf.getSessionExpires()) +CRLF + "Min-SE: " + int2str(session_timer_conf.getMinimumTimer()) + CRLF; end: hdrs += m_hdrs; return false; }
int AmB2BSession::relaySip(const AmSipRequest& orig, const AmSipReply& reply) { const string* hdrs = &reply.hdrs; string m_hdrs; const string method(orig.method); if (reply.rseq != 0) { m_hdrs = reply.hdrs + SIP_HDR_COLSP(SIP_HDR_RSEQ) + int2str(reply.rseq) + CRLF; hdrs = &m_hdrs; } AmMimeBody body(reply.body); if ((orig.method == SIP_METH_INVITE || orig.method == SIP_METH_UPDATE || orig.method == SIP_METH_ACK || orig.method == SIP_METH_PRACK)) { updateLocalBody(body); } DBG("relaying SIP reply %u %s\n", reply.code, reply.reason.c_str()); int flags = SIP_FLAGS_VERBATIM; if(reply.to_tag.empty()) flags |= SIP_FLAGS_NOTAG; int err = dlg->reply(orig,reply.code,reply.reason, &body, *hdrs, flags); if(err < 0){ ERROR("dlg->reply() failed\n"); return err; } if ((method == SIP_METH_INVITE || method == SIP_METH_UPDATE) && !reply.body.empty()) { saveSessionDescription(reply.body); } return 0; }
string AmBasicSipDialog::getRoute() { string res; if(!outbound_proxy.empty() && (force_outbound_proxy || remote_tag.empty())){ res += "<" + outbound_proxy + ";lr>"; if(!route.empty()) { res += ","; } } res += route; if(!res.empty()) { res = SIP_HDR_COLSP(SIP_HDR_ROUTE) + res + CRLF; } return res; }
string AmBasicSipDialog::getContactHdr() { AmUriParser tmp_contact = contact; if(tmp_contact.uri_host.empty()) { int oif = getOutboundIf(); assert(oif >= 0); assert(oif < (int)AmConfig::SIP_Ifs.size()); tmp_contact.uri_host = AmConfig::SIP_Ifs[oif].getIP(); tmp_contact.uri_port = int2str(AmConfig::SIP_Ifs[oif].LocalPort); } if(tmp_contact.uri_user.empty() && !ext_local_tag.empty()) { tmp_contact.uri_user = local_tag; } string contact_str = tmp_contact.print(); DBG("[%s] resulting Contact header: %s", local_tag.c_str(), contact_str.c_str()); return SIP_HDR_COLSP(SIP_HDR_CONTACT) + contact_str += CRLF; }
bool AmBasicSipDialog::onRxReqSanity(const AmSipRequest& req) { // Sanity checks if(!remote_tag.empty() && !req.from_tag.empty() && (req.from_tag != remote_tag)){ DBG("remote_tag = '%s'; req.from_tag = '%s'\n", remote_tag.c_str(), req.from_tag.c_str()); reply_error(req, 481, SIP_REPLY_NOT_EXIST); return false; } if (r_cseq_i && req.cseq <= r_cseq){ if (req.method == SIP_METH_NOTIFY) { if (!AmConfig::IgnoreNotifyLowerCSeq) { // clever trick to not break subscription dialog usage // for implementations which follow 3265 instead of 5057 string hdrs = SIP_HDR_COLSP(SIP_HDR_RETRY_AFTER) "0" CRLF; INFO("remote cseq lower than previous ones - refusing request\n"); // see 12.2.2 reply_error(req, 500, SIP_REPLY_SERVER_INTERNAL_ERROR, hdrs); return false; } } else { INFO("remote cseq lower than previous ones - refusing request\n"); // see 12.2.2 reply_error(req, 500, SIP_REPLY_SERVER_INTERNAL_ERROR); return false; } } r_cseq = req.cseq; r_cseq_i = true; return true; }
int AmBasicSipDialog::sendRequest(const string& method, const AmMimeBody* body, const string& hdrs, int flags) { AmSipRequest req; req.method = method; req.r_uri = remote_uri; req.from = SIP_HDR_COLSP(SIP_HDR_FROM) + local_party; if(!ext_local_tag.empty()) req.from += ";tag=" + ext_local_tag; else if(!local_tag.empty()) req.from += ";tag=" + local_tag; req.to = SIP_HDR_COLSP(SIP_HDR_TO) + remote_party; if(!remote_tag.empty()) req.to += ";tag=" + remote_tag; req.cseq = cseq; req.callid = callid; req.hdrs = hdrs; req.route = getRoute(); if(body != NULL) { req.body = *body; } if(onTxRequest(req,flags) < 0) return -1; if (!(flags & SIP_FLAGS_NOCONTACT)) { req.contact = getContactHdr(); } if (!(flags & SIP_FLAGS_VERBATIM)) { // add Signature if (AmConfig::Signature.length()) req.hdrs += SIP_HDR_COLSP(SIP_HDR_USER_AGENT) + AmConfig::Signature + CRLF; } int send_flags = 0; if(patch_ruri_next_hop && remote_tag.empty()) { send_flags |= TR_FLAG_NEXT_HOP_RURI; } if((flags & SIP_FLAGS_NOBL) || !remote_tag.empty()) { send_flags |= TR_FLAG_DISABLE_BL; } int res = SipCtrlInterface::send(req, local_tag, remote_tag.empty() || !next_hop_1st_req ? next_hop : "", outbound_interface, send_flags,logger); if(res) { ERROR("Could not send request: method=%s; call-id=%s; cseq=%i\n", req.method.c_str(),req.callid.c_str(),req.cseq); return res; } onRequestTxed(req); return 0; }
int AmBasicSipDialog::reply(const AmSipRequest& req, unsigned int code, const string& reason, const AmMimeBody* body, const string& hdrs, int flags) { TransMap::const_iterator t_it = uas_trans.find(req.cseq); if(t_it == uas_trans.end()){ ERROR("could not find any transaction matching request cseq\n"); ERROR("request cseq=%i; reply code=%i; callid=%s; local_tag=%s; " "remote_tag=%s\n", req.cseq,code,callid.c_str(), local_tag.c_str(),remote_tag.c_str()); log_stacktrace(L_ERR); return -1; } DBG("reply: transaction found!\n"); AmSipReply reply; reply.code = code; reply.reason = reason; reply.tt = req.tt; if((code > 100) && !(flags & SIP_FLAGS_NOTAG)) reply.to_tag = ext_local_tag.empty() ? local_tag : ext_local_tag; reply.hdrs = hdrs; reply.cseq = req.cseq; reply.cseq_method = req.method; if(body != NULL) reply.body = *body; if(onTxReply(req,reply,flags)){ DBG("onTxReply failed\n"); return -1; } if (!(flags & SIP_FLAGS_VERBATIM)) { // add Signature if (AmConfig::Signature.length()) reply.hdrs += SIP_HDR_COLSP(SIP_HDR_SERVER) + AmConfig::Signature + CRLF; } if ((code > 100 && code < 300) && !(flags & SIP_FLAGS_NOCONTACT)) { /* if 300<=code<400, explicit contact setting should be done */ reply.contact = getContactHdr(); } int ret = SipCtrlInterface::send(reply,local_tag,logger); if(ret){ ERROR("Could not send reply: code=%i; reason='%s'; method=%s;" " call-id=%s; cseq=%i\n", reply.code,reply.reason.c_str(),reply.cseq_method.c_str(), callid.c_str(),reply.cseq); return ret; } else { onReplyTxed(req,reply); } return ret; }
string AmBasicSipDialog::getContactHdr() { return SIP_HDR_COLSP(SIP_HDR_CONTACT) "<"+ getContactUri() += ">" CRLF; }
void fixReplaces(string& req_hdrs, bool is_invite) { string replaces; string refer_to; AmUriParser refer_target; vector<string> hdrs; // headers from Refer-To URI vector<string>::iterator replaces_hdr_it; // Replaces header from Refer-To URI DBG("Replaces handler: fixing %s request\n", is_invite?"INVITE":"REFER"); if (is_invite) { replaces = getHeader(req_hdrs, SIP_HDR_REPLACES, true); if (replaces.empty()) { DBG("Replaces handler: no Replaces in INVITE, ignoring\n"); return; } } else { refer_to = getHeader(req_hdrs, SIP_HDR_REFER_TO, SIP_HDR_REFER_TO_COMPACT, true); if (refer_to.empty()) { DBG("Replaces handler: empty Refer-To header, ignoring\n"); return; } size_t pos=0; size_t end=0; if (!refer_target.parse_contact(refer_to, pos, end)) { DBG("Replaces handler: unable to parse Refer-To name-addr, ignoring\n"); return; } if (refer_target.uri_headers.empty()) { DBG("Replaces handler: no headers in Refer-To target, ignoring\n"); return; } hdrs = explode(refer_target.uri_headers, ";"); for (replaces_hdr_it=hdrs.begin(); replaces_hdr_it != hdrs.end(); replaces_hdr_it++) { string s = URL_decode(*replaces_hdr_it); const char* Replaces_str = "Replaces"; if ((s.length() >= 8) && !strncmp(Replaces_str, s.c_str(), 8)) { size_t pos = 8; while (s.length()>pos && (s[pos] == ' ' || s[pos] == '\t')) pos++; if (s[pos] != '=') continue; pos++; while (s.length()>pos && (s[pos] == ' ' || s[pos] == '\t')) pos++; replaces = s.substr(pos); break; } } if (replaces_hdr_it == hdrs.end()) { DBG("Replaces handler: no Replaces headers in Refer-To target, ignoring\n"); return; } } DBG("Replaces found: '%s'\n", replaces.c_str()); size_t ftag_begin; size_t ftag_len; size_t ttag_begin; size_t ttag_len; size_t cid_len=0; // todo: parse full replaces header and reconstruct including unknown params if (!findTag(replaces, "from-tag=", ftag_begin, ftag_len)) { WARN("Replaces missing 'from-tag', ignoring\n"); return; } if (!findTag(replaces, "to-tag=", ttag_begin, ttag_len)) { WARN("Replaces missing 'to-tag', ignoring\n"); return; } while (cid_len < replaces.size() && replaces[cid_len] != ';') cid_len++; string ftag = replaces.substr(ftag_begin, ftag_len); string ttag = replaces.substr(ttag_begin, ttag_len); string callid = replaces.substr(0, cid_len); bool early_only = replaces.find("early-only") != string::npos; DBG("Replaces handler: found callid='%s', ftag='%s', ttag='%s'\n", callid.c_str(), ftag.c_str(), ttag.c_str()); SBCCallRegistryEntry other_dlg; if (SBCCallRegistry::lookupCall(ttag, other_dlg)) { replaces = other_dlg.callid+ ";from-tag="+other_dlg.ltag+";to-tag="+other_dlg.rtag; if (early_only) replaces += ";early_only"; DBG("Replaces handler: mapped Replaces to: '%s'\n", replaces.c_str()); if (is_invite) { removeHeader(req_hdrs, SIP_HDR_REPLACES); req_hdrs+=SIP_HDR_COLSP(SIP_HDR_REPLACES)+replaces+CRLF; } else { string replaces_enc = SIP_HDR_REPLACES "="+URL_encode(replaces); string new_hdrs; for (vector<string>::iterator it = hdrs.begin(); it != hdrs.end(); it++) { if (it != hdrs.begin()) new_hdrs+=";"; if (it != replaces_hdr_it) { // different hdr, just add it new_hdrs+=*it; } else { //reconstructed replaces hdr new_hdrs+=replaces_enc; } } refer_target.uri_headers=new_hdrs; removeHeader(req_hdrs, SIP_HDR_REFER_TO); removeHeader(req_hdrs, SIP_HDR_REFER_TO_COMPACT); req_hdrs+=SIP_HDR_COLSP(SIP_HDR_REFER_TO)+refer_target.nameaddr_str()+CRLF; } } else { DBG("Replaces handler: call with tag '%s' not found\n", ttag.c_str()); } }
int AmB2BSession::relaySip(const AmSipRequest& req) { AmMimeBody body(req.body); if ((req.method == SIP_METH_INVITE || req.method == SIP_METH_UPDATE || req.method == SIP_METH_ACK || req.method == SIP_METH_PRACK)) { updateLocalBody(body); } if (req.method != "ACK") { relayed_req[dlg->cseq] = req; const string* hdrs = &req.hdrs; string m_hdrs; // translate RAck for PRACK if (req.method == SIP_METH_PRACK && req.rseq) { TransMap::iterator t; for (t=relayed_req.begin(); t != relayed_req.end(); t++) { if (t->second.cseq == req.rack_cseq) { m_hdrs = req.hdrs + SIP_HDR_COLSP(SIP_HDR_RACK) + int2str(req.rseq) + " " + int2str(t->first) + " " + req.rack_method + CRLF; hdrs = &m_hdrs; break; } } if (t==relayed_req.end()) { WARN("Transaction with CSeq %d not found for translating RAck cseq\n", req.rack_cseq); } } DBG("relaying SIP request %s %s\n", req.method.c_str(), req.r_uri.c_str()); int err = dlg->sendRequest(req.method, &body, *hdrs, SIP_FLAGS_VERBATIM); if(err < 0){ ERROR("dlg->sendRequest() failed\n"); return err; } if ((req.method == SIP_METH_INVITE || req.method == SIP_METH_UPDATE) && !req.body.empty()) { saveSessionDescription(req.body); } } else { //its a (200) ACK TransMap::iterator t = relayed_req.begin(); while (t != relayed_req.end()) { if (t->second.cseq == req.cseq) break; t++; } if (t == relayed_req.end()) { ERROR("transaction for ACK not found in relayed requests\n"); // FIXME: local body (if updated) should be discarded here return -1; } DBG("sending relayed 200 ACK\n"); int err = dlg->send_200_ack(t->first, &body, req.hdrs, SIP_FLAGS_VERBATIM); if(err < 0) { ERROR("dlg->send_200_ack() failed\n"); return err; } if (!req.body.empty() && (t->second.method == SIP_METH_INVITE)) { // delayed SDP negotiation - save SDP saveSessionDescription(req.body); } relayed_req.erase(t); } return 0; }
void AmB2BSession::onSipRequest(const AmSipRequest& req) { bool fwd = sip_relay_only && (req.method != SIP_METH_CANCEL); if( ((req.method == SIP_METH_SUBSCRIBE) || (req.method == SIP_METH_NOTIFY) || (req.method == SIP_METH_REFER)) && !subs->onRequestIn(req) ) { return; } if(!fwd) AmSession::onSipRequest(req); else { updateRefreshMethod(req.hdrs); if(req.method == SIP_METH_BYE) onBye(req); } B2BSipRequestEvent* r_ev = new B2BSipRequestEvent(req,fwd); if (fwd) { DBG("relaying B2B SIP request (fwd) %s %s\n", r_ev->req.method.c_str(), r_ev->req.r_uri.c_str()); if(r_ev->req.method == SIP_METH_NOTIFY) { string event = getHeader(r_ev->req.hdrs,SIP_HDR_EVENT,true); string id = get_header_param(event,"id"); event = strip_header_params(event); if(event == "refer" && !id.empty()) { int id_int=0; if(str2int(id,id_int)) { unsigned int mapped_id=0; if(getMappedReferID(id_int,mapped_id)) { removeHeader(r_ev->req.hdrs,SIP_HDR_EVENT); r_ev->req.hdrs += SIP_HDR_COLSP(SIP_HDR_EVENT) "refer;id=" + int2str(mapped_id) + CRLF; } } } } int res = relayEvent(r_ev); if (res == 0) { // successfuly relayed, store the request if(req.method != SIP_METH_ACK) recvd_req.insert(std::make_pair(req.cseq,req)); } else { // relay failed, generate error reply DBG("relay failed, replying error\n"); AmSipReply n_reply; errCode2RelayedReply(n_reply, res, 500); dlg->reply(req, n_reply.code, n_reply.reason); } return; } DBG("relaying B2B SIP request %s %s\n", r_ev->req.method.c_str(), r_ev->req.r_uri.c_str()); relayEvent(r_ev); }
void AmB2BSession::onB2BEvent(B2BEvent* ev) { DBG("AmB2BSession::onB2BEvent\n"); switch(ev->event_id){ case B2BSipRequest: { B2BSipRequestEvent* req_ev = dynamic_cast<B2BSipRequestEvent*>(ev); assert(req_ev); DBG("B2BSipRequest: %s (fwd=%s)\n", req_ev->req.method.c_str(), req_ev->forward?"true":"false"); if(req_ev->forward){ // Check Max-Forwards first if(req_ev->req.max_forwards == 0) { relayError(req_ev->req.method,req_ev->req.cseq, true,483,SIP_REPLY_TOO_MANY_HOPS); return; } if (req_ev->req.method == SIP_METH_INVITE && dlg->getUACInvTransPending()) { // don't relay INVITE if INV trans pending DBG("not sip-relaying INVITE with pending INV transaction, " "b2b-relaying 491 pending\n"); relayError(req_ev->req.method, req_ev->req.cseq, true, 491, SIP_REPLY_PENDING); return; } if (req_ev->req.method == SIP_METH_BYE && dlg->getStatus() != AmBasicSipDialog::Connected) { DBG("not sip-relaying BYE in not connected dlg, b2b-relaying 200 OK\n"); relayError(req_ev->req.method, req_ev->req.cseq, true, 200, "OK"); return; } } if( (req_ev->req.method == SIP_METH_BYE) // CANCEL is handled differently: other side has already // sent a terminate event. //|| (req_ev->req.method == SIP_METH_CANCEL) ) { if (onOtherBye(req_ev->req)) req_ev->processed = true; // app should have relayed 200 to BYE } if(req_ev->forward && !req_ev->processed){ int res = relaySip(req_ev->req); if(res < 0) { // reply relayed request internally relayError(req_ev->req.method, req_ev->req.cseq, true, res); return; } } } return; case B2BSipReply: { B2BSipReplyEvent* reply_ev = dynamic_cast<B2BSipReplyEvent*>(ev); assert(reply_ev); DBG("B2BSipReply: %i %s (fwd=%s)\n",reply_ev->reply.code, reply_ev->reply.reason.c_str(),reply_ev->forward?"true":"false"); DBG("B2BSipReply: content-type = %s\n", reply_ev->reply.body.getCTStr().c_str()); if(reply_ev->forward){ std::map<int,AmSipRequest>::iterator t_req = recvd_req.find(reply_ev->reply.cseq); if (t_req != recvd_req.end()) { if ((reply_ev->reply.code >= 300) && (reply_ev->reply.code <= 305) && !reply_ev->reply.contact.empty()) { // relay with Contact in 300 - 305 redirect messages AmSipReply n_reply(reply_ev->reply); n_reply.hdrs+=SIP_HDR_COLSP(SIP_HDR_CONTACT) + reply_ev->reply.contact+ CRLF; if(relaySip(t_req->second,n_reply) < 0) { terminateOtherLeg(); terminateLeg(); } } else { // relay response if(relaySip(t_req->second,reply_ev->reply) < 0) { terminateOtherLeg(); terminateLeg(); } } } else { DBG("Cannot relay reply: request already replied" " (code=%u;cseq=%u;call-id=%s)", reply_ev->reply.code, reply_ev->reply.cseq, reply_ev->reply.callid.c_str()); } } else { // check whether not-forwarded (locally initiated) // INV/UPD transaction changed session in other leg if (SIP_IS_200_CLASS(reply_ev->reply.code) && (!reply_ev->reply.body.empty()) && (reply_ev->reply.cseq_method == SIP_METH_INVITE || reply_ev->reply.cseq_method == SIP_METH_UPDATE)) { if (updateSessionDescription(reply_ev->reply.body)) { if (reply_ev->reply.cseq != est_invite_cseq) { if (dlg->getUACInvTransPending()) { DBG("changed session, but UAC INVITE trans pending\n"); // todo(?): save until trans is finished? return; } DBG("session description changed - refreshing\n"); sendEstablishedReInvite(); } else { DBG("reply to establishing INVITE request - not refreshing\n"); } } } } } return; case B2BTerminateLeg: DBG("terminateLeg()\n"); terminateLeg(); break; } //ERROR("unknown event caught\n"); }