/*
* RequestValidation.java
*
* Created on April 10, 2003, 6:31 PM
*/
package org.cafesip.jiplet.sip;
import java.util.ListIterator;
import javax.sip.ServerTransaction;
import javax.sip.SipProvider;
import javax.sip.address.URI;
import javax.sip.header.EventHeader;
import javax.sip.header.FromHeader;
import javax.sip.header.HeaderFactory;
import javax.sip.header.MaxForwardsHeader;
import javax.sip.header.ProxyRequireHeader;
import javax.sip.header.UnsupportedHeader;
import javax.sip.header.ViaHeader;
import javax.sip.message.MessageFactory;
import javax.sip.message.Request;
import javax.sip.message.Response;
import org.cafesip.jiplet.Jiplet;
/**
* RFC 3261 16.3 Request Validation: Before an element can proxy a request, it
* MUST verify the message's validity. A valid message must pass the following
* checks: 1. Reasonable Syntax: Here, it is done by the stack, so this step is
* passed!
*
* 2. URI scheme: we just support the "sip", "sips", "tel" schemes, for
* simplicity!!!
*
* 3. Max-Forwards
*
* 4. (Optional) Loop Detection
*
* 5. Proxy-Require: We don't support any option tags!
*
* 6. Proxy-Authorization
*
* @author deruelle
*/
public class RequestValidation
{
private Jiplet proxy;
private boolean presenceServer;
/** Creates a new instance of RequestValidation */
protected RequestValidation(Jiplet proxy, boolean presenceServer)
{
this.proxy = proxy;
this.presenceServer = presenceServer;
}
protected boolean validateRequest(SipProvider sipProvider, Request request,
ServerTransaction serverTransaction)
{
try
{
MessageFactory messageFactory = proxy.getMessageFactory();
// Important check: the server transaction can be null! So, in this
// case,
// we have to reply an eventual error code statelessly
if (!checkURIScheme(request))
{
// Let's return a 416 Unsupported URI scheme
Response response = messageFactory.createResponse(
Response.UNSUPPORTED_URI_SCHEME, request);
if (serverTransaction != null)
serverTransaction.sendResponse(response);
else
sipProvider.sendResponse(response);
return false;
}
if (!checkMaxForwards(request))
{
// Let's return a 483 too many hops
Response response = messageFactory.createResponse(
Response.TOO_MANY_HOPS, request);
if (serverTransaction != null)
serverTransaction.sendResponse(response);
else
sipProvider.sendResponse(response);
return false;
}
if (!checkLoopDetection(request))
{
// Let's return a 482 Loop detection
Response response = messageFactory.createResponse(
Response.LOOP_DETECTED, request);
if (serverTransaction != null)
serverTransaction.sendResponse(response);
else
sipProvider.sendResponse(response);
return false;
}
if (!checkProxyRequire(request))
{
// Let's return a 420 Bad Extension
Response response = messageFactory.createResponse(
Response.BAD_EXTENSION, request);
// We have to add a Unsupported header listing the Option tags
// that we don't support:
HeaderFactory headerFactory = proxy.getHeaderFactory();
ProxyRequireHeader prh = (ProxyRequireHeader) request
.getHeader(ProxyRequireHeader.NAME);
if (prh != null)
{
UnsupportedHeader unsupportedHeader = headerFactory
.createUnsupportedHeader(prh.getOptionTag());
response.setHeader(unsupportedHeader);
}
if (serverTransaction != null)
serverTransaction.sendResponse(response);
else
sipProvider.sendResponse(response);
return false;
}
// Let's add some more important basics checks:
// - From tag presence.
if (!checkFromTag(request))
{
// Let's return a 400 BAD_REQUEST
Response response = messageFactory.createResponse(
Response.BAD_REQUEST, request);
if (serverTransaction != null)
serverTransaction.sendResponse(response);
else
sipProvider.sendResponse(response);
return false;
}
// For Event notifications:
if (presenceServer)
{
String method = request.getMethod();
if (method.equals("SUBSCRIBE"))
{
// RFC 3265 3.1.1:
/*
* Subscribers MUST include exactly one "Event" header in
* SUBSCRIBE requests, indicating to which event or class of
* events they are subscribing.
*/
if (!checkEventHeader(request))
{
// Let's return a 400 BAD_REQUEST
Response response = messageFactory.createResponse(
Response.BAD_REQUEST, request);
if (serverTransaction != null)
serverTransaction.sendResponse(response);
else
sipProvider.sendResponse(response);
return false;
}
}
}
// All the checks are passed:
return true;
}
catch (Exception e)
{
return false;
}
}
private boolean checkURIScheme(Request request)
{
try
{
URI requestURI = request.getRequestURI();
String uriScheme = requestURI.getScheme();
return uriScheme.equals("sip") || uriScheme.equals("sips")
|| uriScheme.equals("tel");
}
catch (Exception e)
{
return false;
}
}
private boolean checkMaxForwards(Request request)
{
try
{
MaxForwardsHeader mf = (MaxForwardsHeader) request
.getHeader(MaxForwardsHeader.NAME);
if (mf == null)
{
// We don't add one here!!! We will do it on the cloned request
// that we are going to forward later.
return true;
}
if (mf.getMaxForwards() == 0)
{
return false;
}
else
{
// We don't decrement here!!! We will do it on the cloned
// request
// that we are going to forward later.
return true;
}
}
catch (Exception e)
{
return false;
}
}
private boolean checkLoopDetection(Request request)
{
try
{
ListIterator viaList = request.getHeaders(ViaHeader.NAME);
if (viaList == null)
return false;
while (viaList.hasNext())
{
ViaHeader viaHeader = (ViaHeader) viaList.next();
if (proxy.hasAddress(viaHeader.getHost(), viaHeader.getPort()) == true)
{
// We have to check the branch-ids...
// TODO.
return false;
}
}
return true;
}
catch (Exception e)
{
return false;
}
}
private boolean checkProxyRequire(Request request)
{
try
{
ProxyRequireHeader prh = (ProxyRequireHeader) request
.getHeader(ProxyRequireHeader.NAME);
if (prh == null)
return true;
else
{
// We don't support any option tags. So we reject the request:
return false;
}
}
catch (Exception e)
{
return false;
}
}
private boolean checkFromTag(Request request)
{
try
{
if (request.getMethod().equals(Request.REGISTER))
return true;
FromHeader fh = (FromHeader) request.getHeader(FromHeader.NAME);
return (fh.getTag() != null);
}
catch (Exception e)
{
return false;
}
}
private boolean checkEventHeader(Request request)
{
try
{
EventHeader eventHeader = (EventHeader) request
.getHeader(EventHeader.NAME);
// return eventHeader!=null;
// For Microsoft interoperability:
return true;
}
catch (Exception e)
{
return false;
}
}
}