/*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.mobicents.servlet.sip.proxy;
import gov.nist.javax.sip.header.HeaderFactoryExt;
import gov.nist.javax.sip.header.ims.PathHeader;
import gov.nist.javax.sip.message.SIPMessage;
import java.util.Iterator;
import java.util.Random;
import javax.sip.ListeningPoint;
import javax.sip.address.Address;
import javax.sip.header.MaxForwardsHeader;
import javax.sip.header.RecordRouteHeader;
import javax.sip.header.ViaHeader;
import javax.sip.message.Request;
import javax.sip.message.Response;
import org.apache.log4j.Logger;
import org.mobicents.servlet.sip.JainSipUtils;
import org.mobicents.servlet.sip.SipFactories;
import org.mobicents.servlet.sip.address.SipURIImpl;
import org.mobicents.servlet.sip.address.URIImpl;
import org.mobicents.servlet.sip.core.dispatchers.MessageDispatcher;
import org.mobicents.servlet.sip.core.session.SipApplicationSessionKey;
import org.mobicents.servlet.sip.message.SipFactoryImpl;
import org.mobicents.servlet.sip.message.SipServletMessageImpl;
import org.mobicents.servlet.sip.message.SipServletRequestImpl;
import org.mobicents.servlet.sip.message.SipServletResponseImpl;
/**
* TODO: Use outbound interface from ProxyParams.outboundInterface when adding local
* listening point addresses.
*
*/
public class ProxyUtils {
private static transient Logger logger = Logger.getLogger(ProxyUtils.class);
private SipFactoryImpl sipFactoryImpl;
private ProxyImpl proxy;
private static transient Random random = new Random(System.currentTimeMillis());
public ProxyUtils(SipFactoryImpl sipFactoryImpl, ProxyImpl proxy) {
this.sipFactoryImpl = sipFactoryImpl;
this.proxy = proxy;
}
public Request createProxiedRequest(SipServletRequestImpl originalRequest, ProxyBranchImpl proxyBranch, ProxyParams params)
{
try {
Request clonedRequest = (Request) originalRequest.getMessage().clone();
((SIPMessage)clonedRequest).setApplicationData(null);
// The target is null when proxying subsequent requests (the Route header is already there)
if(params.destination != null)
{
if(logger.isDebugEnabled()){
logger.debug("request URI on the request to proxy : " + params.destination);
}
//this way everything is copied even the port but might not work for TelURI...
clonedRequest.setRequestURI(((URIImpl)params.destination).getURI());
// // Add route header
// javax.sip.address.SipURI routeUri = SipFactories.addressFactory.createSipURI(
// params.destination.getUser(), params.destination.getHost());
// routeUri.setPort(params.destination.getPort());
// routeUri.setLrParam();
// javax.sip.address.Address address = SipFactories.addressFactory.createAddress(params.destination.getUser(),
// routeUri);
// RouteHeader rheader = SipFactories.headerFactory.createRouteHeader(address);
//
// clonedRequest.setHeader(rheader);
}
else
{
// CANCELs are hop-by-hop, so here must remove any existing Via
// headers,
// Record-Route headers. We insert Via header below so we will
// get response.
if (clonedRequest.getMethod().equals(Request.CANCEL)) {
clonedRequest.removeHeader(ViaHeader.NAME);
clonedRequest.removeHeader(RecordRouteHeader.NAME);
}
}
// Decrease max forwards if available
MaxForwardsHeader mf = (MaxForwardsHeader) clonedRequest
.getHeader(MaxForwardsHeader.NAME);
if (mf == null) {
mf = SipFactories.headerFactory.createMaxForwardsHeader(70);
clonedRequest.addHeader(mf);
} else {
mf.setMaxForwards(mf.getMaxForwards() - 1);
}
if (clonedRequest.getMethod().equals(Request.CANCEL)) {
// Cancel is hop by hop so remove all other via headers.
clonedRequest.removeHeader(ViaHeader.NAME);
}
//Add via header
ViaHeader viaHeader = proxyBranch.viaHeader;
if(viaHeader == null) {
if(proxy.getOutboundInterface() == null) {
String branchId = null;
if(Request.ACK.equals(clonedRequest.getMethod()) && proxyBranch.getRequest() != null && ((SipServletMessageImpl)proxyBranch.getRequest()).getTransaction() != null) {
branchId = ((SipServletMessageImpl)proxyBranch.getRequest()).getTransaction().getBranchId();
logger.debug("reusing original branch id " + branchId);
}
viaHeader = JainSipUtils.createViaHeader(
sipFactoryImpl.getSipNetworkInterfaceManager(), clonedRequest, branchId);
} else {
//If outbound interface is specified use it
String outboundTransport = proxy.getOutboundInterface().getTransportParam();
if(outboundTransport == null) {
outboundTransport = ListeningPoint.UDP;
}
String branchId = null;
if(Request.ACK.equals(clonedRequest.getMethod()) && proxyBranch.getRequest() != null && ((SipServletMessageImpl)proxyBranch.getRequest()).getTransaction() != null) {
branchId = ((SipServletMessageImpl)proxyBranch.getRequest()).getTransaction().getBranchId();
logger.debug("reusing original branch id " + branchId);
}
viaHeader = SipFactories.headerFactory.createViaHeader(
proxy.getOutboundInterface().getHost(),
proxy.getOutboundInterface().getPort(),
outboundTransport,
branchId);
}
proxyBranch.viaHeader = viaHeader;
} else {
String branchId = null;
viaHeader = (ViaHeader) viaHeader.clone();
if(Request.ACK.equals(clonedRequest.getMethod()) && proxyBranch.getRequest() != null && ((SipServletMessageImpl)proxyBranch.getRequest()).getTransaction() != null) {
branchId = ((SipServletMessageImpl)proxyBranch.getRequest()).getTransaction().getBranchId();
logger.debug("reusing original branch id " + branchId);
} else {
branchId = "z9hG4bK" + Long.toHexString(System.nanoTime()^32543621) + Integer.toString(random.nextInt());
}
viaHeader.setBranch(branchId);
}
clonedRequest.addHeader(viaHeader);
//Add route-record header, if enabled and if needed (if not null)
if(params.routeRecord != null && !Request.REGISTER.equalsIgnoreCase(originalRequest.getMethod())) {
javax.sip.address.SipURI rrURI = null;
if(proxy.getOutboundInterface() == null) {
rrURI = JainSipUtils.createRecordRouteURI(sipFactoryImpl.getSipNetworkInterfaceManager(), clonedRequest);
} else {
rrURI = ((SipURIImpl) proxy.getOutboundInterface()).getSipURI();
}
Iterator<String> paramNames = params.routeRecord.getParameterNames();
// Copy the parameters set by the user
while(paramNames.hasNext()) {
String paramName = paramNames.next();
rrURI.setParameter(paramName,
params.routeRecord.getParameter(paramName));
}
SipApplicationSessionKey sipAppKey = originalRequest.getSipSession().getSipApplicationSession().getKey();
rrURI.setParameter(MessageDispatcher.RR_PARAM_APPLICATION_NAME,
proxy.getSipFactoryImpl().getSipApplicationDispatcher().getHashFromApplicationName(sipAppKey.getApplicationName()));
rrURI.setParameter(MessageDispatcher.RR_PARAM_PROXY_APP,
"true");
rrURI.setParameter(MessageDispatcher.APP_ID, sipAppKey.getId());
rrURI.setLrParam();
Address rraddress = SipFactories.addressFactory
.createAddress(null, rrURI);
RecordRouteHeader recordRouteHeader = SipFactories.headerFactory
.createRecordRouteHeader(rraddress);
clonedRequest.addFirst(recordRouteHeader);
}
// Add path header
if(params.path != null && Request.REGISTER.equalsIgnoreCase(originalRequest.getMethod()))
{
javax.sip.address.SipURI pathURI = JainSipUtils.createRecordRouteURI(sipFactoryImpl.getSipNetworkInterfaceManager(), clonedRequest);
Iterator<String> paramNames = params.path.getParameterNames();
// Copy the parameters set by the user
while(paramNames.hasNext()) {
String paramName = paramNames.next();
pathURI.setParameter(paramName,
params.path.getParameter(paramName));
}
Address pathAddress = SipFactories.addressFactory
.createAddress(null, pathURI);
// Here I need to reference the header factory impl class because can't create path header otherwise
PathHeader pathHeader = ((HeaderFactoryExt)SipFactories.headerFactory)
.createPathHeader(pathAddress);
clonedRequest.addFirst(pathHeader);
}
return clonedRequest;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
public SipServletResponseImpl createProxiedResponse(SipServletResponseImpl sipServetResponse, ProxyBranchImpl proxyBranch)
{
Response response = (Response)sipServetResponse.getMessage();
Response clonedResponse = (Response) response.clone();
((SIPMessage)clonedResponse).setApplicationData(null);
// 1. Update timer C for provisional non retransmission responses
if(sipServetResponse.getTransaction() != null && sipServetResponse.getTransaction().getRequest().getMethod().equals(Request.INVITE)) {
if(Response.TRYING < response.getStatusCode() && response.getStatusCode() < Response.OK) {
proxyBranch.updateTimer();
} else if(response.getStatusCode() >= Response.OK) {
//remove it if response is final
proxyBranch.cancelTimer();
}
}
// 2. Remove topmost via
Iterator<ViaHeader> viaHeaderIt = clonedResponse.getHeaders(ViaHeader.NAME);
viaHeaderIt.next();
viaHeaderIt.remove();
if (!viaHeaderIt.hasNext()) {
return null; // response was meant for this proxy
}
SipServletRequestImpl originalRequest =
(SipServletRequestImpl) this.proxy.getOriginalRequest();
if(Request.PRACK.equals(sipServetResponse.getMethod())) {
originalRequest =
(SipServletRequestImpl) proxyBranch.getPrackOriginalRequest();
}
SipServletResponseImpl newServletResponseImpl = null;
if(sipServetResponse.getTransaction() != null) {
// non retransmission case
newServletResponseImpl = new SipServletResponseImpl(clonedResponse,
sipFactoryImpl,
originalRequest.getTransaction(),
originalRequest.getSipSession(),
sipServetResponse.getDialog());
} else {
// retransmission case
newServletResponseImpl = new SipServletResponseImpl(clonedResponse,
sipFactoryImpl,
null,
sipServetResponse.getSipSession(),
sipServetResponse.getDialog());
}
newServletResponseImpl.setOriginalRequest(originalRequest);
newServletResponseImpl.setProxiedResponse(true);
return newServletResponseImpl;
}
public static String toHexString(byte[] b) {
int pos = 0;
char[] c = new char[b.length * 2];
for (int i = 0; i < b.length; i++) {
c[pos++] = toHex[(b[i] >> 4) & 0x0F];
c[pos++] = toHex[b[i] & 0x0f];
}
return new String(c);
}
private static final char[] toHex = { '0', '1', '2', '3', '4', '5', '6',
'7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
}