examples.subsnotify.Forker Maven / Gradle / Ivy
package examples.subsnotify;
import javax.sip.*;
import javax.sip.address.*;
import javax.sip.header.*;
import javax.sip.message.*;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.FileAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.SimpleLayout;
import java.text.ParseException;
import java.util.*;
/**
* This implements a simple forking proxy to test proper handling of multiple
* NOTIFYs. An initial SUBSCRIBE request (i.e. without to-tag) is forked two
* times to the same destination. Each response should have a different to-tag;
* this proxy only passes the first 2xx response through, and discards the
* second
*
* NOTIFYs should go directly to the Contact announced in the SUBSCRIBE, hence
* this proxy won't see them
*
* @author Jeroen van Bemmel
*/
public class Forker implements SipListener {
private static AddressFactory addressFactory;
private static MessageFactory messageFactory;
private static HeaderFactory headerFactory;
private static SipStack sipStack;
private SipProvider udpProvider;
/**
* Flag to test UAC behavior for non-RFC3261 proxies. In particular, they dont
* set the 'lr' flag and perform strict routing, ie replace the request URI
* with the topmost Route header
*/
private static boolean nonRFC3261Proxy;
private static Logger logger = Logger.getLogger(Forker.class);
static {
try {
logger.setLevel(Level.INFO);
logger.addAppender(new ConsoleAppender(new SimpleLayout()));
logger.addAppender(new FileAppender(new SimpleLayout(),
"forkeroutputlog.txt"));
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
private static final String usageString = "java "
+ "examples.subsnotify.Forker \n"
+ ">>>> is your class path set to the root?";
private static void usage() {
logger.info(usageString);
System.exit(0);
}
/**
* Adds a suitable Record-Route header to the given request or response
*
* @param r
* @throws ParseException
* @throws SipException
* @throws
*/
private void recordRoute( Message m, String uniqueId ) throws ParseException, SipException {
Address me = addressFactory.createAddress( "' );
if (!nonRFC3261Proxy) ((SipURI) me.getURI()).setLrParam();
RecordRouteHeader rr = headerFactory.createRecordRouteHeader(me);
m.addFirst( rr );
}
public void processRequest(RequestEvent re) {
Request request = re.getRequest();
ServerTransaction st = re.getServerTransaction();
logger.info("\n\nRequest " + request.getMethod()
+ " received at " + sipStack.getStackName()
+ " with server transaction id " + st);
try {
if (request.getMethod().equals(Request.SUBSCRIBE)) {
processSubscribe(re, st);
} else if (request.getMethod().equals(Request.NOTIFY)) { // because
// of
// Record-Routing
logger.info( "Got NOTIFY, forwarding statelessly...");
// Forward it without creating a transaction
// RFC3265 says: "proxy MUST record-route the initial SUBSCRIBE
// and
// any dialog-establishing NOTIFY requests
// Use from tag as unique id, for debugging
FromHeader from = (FromHeader) request.getHeader(FromHeader.NAME);
recordRoute( request, from.getTag() );
doForwardStateless( request, st );
} else {
Response notImplemented = messageFactory.createResponse( Response.NOT_IMPLEMENTED, request );
((SipProvider)re.getSource()).sendResponse( notImplemented );
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Process the invite request.
*/
public void processSubscribe( RequestEvent re, ServerTransaction st) {
Request request = re.getRequest();
try {
logger.info("forker: got an Subscribe -> forking or forwarding");
// Check if it is in-dialog or not
ToHeader to = (ToHeader) request.getHeader(ToHeader.NAME);
if (to.getTag()==null) {
logger.info("forker: got a dialog-creating Subscribe forking twice");
if (st==null) {
st = ((SipProvider)re.getSource()).getNewServerTransaction(request);
}
// Subscriber added a Route to us; remove it could check its 'id' here)
request.removeFirst( RouteHeader.NAME );
doFork( request, st, 5070 );
doFork( request, st, 5071 );
} else {
System.out.println("forker: got a mid-dialog Subscribe, forwarding statelessly...");
// Forward it statelessly
doForwardStateless( request, st );
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
/**
* Mapping of Via branch IDs to the corresponding ServerTransaction, used
* for forwarding responses
*/
private final Map CTtoST = new HashMap();
private void doFork( Request orig, ServerTransaction st, int port ) throws Exception {
ViaHeader myVia = headerFactory.createViaHeader( "127.0.0.1",
udpProvider.getListeningPoint("udp").getPort(), "udp", null );
Request forked = (Request) orig.clone();
forked.addHeader( myVia );
// Note: BIG Gotcha: Need to do this before creating the
// ClientTransaction!
if (nonRFC3261Proxy) {
SipURI suri = addressFactory.createSipURI( null, "127.0.0.1" );
suri.setPort( port );
forked.setRequestURI( suri );
} else {
RouteHeader route = headerFactory.createRouteHeader(
addressFactory.createAddress( "" )
);
((SipURI)route.getAddress().getURI()).setPort( port );
forked.addHeader( route );
}
// Add a Record-Route header, to test that separate dialog instances are
// correctly created at the subscriber
// This causes us to receive NOTIFYs too
recordRoute( forked, Integer.toString(port) );
ClientTransaction ct = udpProvider.getNewClientTransaction( forked );
CTtoST.put( ct, st );
ct.sendRequest();// gets sent to the outbound proxy == Notifier
}
private void doForwardStateless( Request orig, ServerTransaction st ) throws ParseException,
InvalidArgumentException, SipException {
// To forward statelessly, we need to keep the stack from
// creating a ST for us.
// Internally a dialog is created for the SUBSCRIBE, unless
// dialog support
// XXX bug: if I use branch==null here, the stack assigns a random int
// without magic cookie
//
// Latest wisdom from RFC3261 says to simply copy branch from current top via
// when forwarding statelessly
//
ViaHeader top = (ViaHeader) orig.getHeader( ViaHeader.NAME );
ViaHeader myVia = headerFactory.createViaHeader("127.0.0.1",
5065, "udp", top.getBranch() );
orig.addFirst( myVia );
if (nonRFC3261Proxy) {
RouteHeader route = (RouteHeader) orig.getHeader( "Route" );
if (route!=null) {
orig.removeFirst( "Route" );
orig.setRequestURI( route.getAddress().getURI() );
}
} else {
orig.removeFirst( RouteHeader.NAME );// points at us
}
// To forward statelessly, we need to keep the stack from creating a ST for us.
// Internally a dialog is created for the SUBSCRIBE, unless dialog support
// is switched off (see initialization)
if (st!=null) {
logger.info( "Would like to forward statelessly, but ST!=null! Problem...");
logger.info("st == " + st);
}
udpProvider.sendRequest( orig );
}
public void processResponse(ResponseEvent responseReceivedEvent) {
logger.info("Got a response");
Response response = (Response) responseReceivedEvent.getResponse();
ClientTransaction ct = responseReceivedEvent.getClientTransaction();
logger.info("Dialog = " + responseReceivedEvent.getDialog());
logger.info("Response received with client transaction id "
+ ct + ": " + response.getStatusCode() );
if (ct==null) {
logger.info( "Assuming NOTIFY response, forwarding...");
// NOTIFYs are forwarded without transaction, do the same for their
// responses
response.removeFirst( ViaHeader.NAME );
try {
udpProvider.sendResponse( response );
} catch (SipException e) {
e.printStackTrace();
}
} else {
ServerTransaction st = (ServerTransaction) CTtoST.get(ct);
if (st!=null) {
// only forward the first response
synchronized (st) {
if (st.getState() == TransactionState.TRYING) {
response.removeFirst( ViaHeader.NAME );
try {
st.sendResponse( response );
} catch (SipException e) {
e.printStackTrace();
} catch (InvalidArgumentException e) {
e.printStackTrace();
}
} else {
logger.info( "Discarding second response" );
}
CTtoST.remove( ct );
}
} else {
logger.info( "No ST found");
}
}
}
public void processTimeout(javax.sip.TimeoutEvent timeoutEvent) {
Transaction transaction;
if (timeoutEvent.isServerTransaction()) {
transaction = timeoutEvent.getServerTransaction();
} else {
transaction = timeoutEvent.getClientTransaction();
}
logger.info("state = " + transaction.getState());
logger.info("Transaction Time out");
}
private static void initFactories () {
SipFactory sipFactory = null;
sipStack = null;
sipFactory = SipFactory.getInstance();
sipFactory.setPathName("gov.nist");
Properties properties = new Properties();
String name = "forker";
if (nonRFC3261Proxy) name += "_nonRFC3261";
properties.setProperty("javax.sip.STACK_NAME", name );
// You need 16 for logging traces. 32 for debug + traces.
// Your code will limp at 32 but it is best for debugging.
properties.setProperty("gov.nist.javax.sip.TRACE_LEVEL", "32");
properties.setProperty("gov.nist.javax.sip.DEBUG_LOG",
"forkerdebug.txt");
properties.setProperty("gov.nist.javax.sip.SERVER_LOG",
"forkerlog.txt");
// Switch OFF automatic dialog support. We dont want dialogs in a proxy!
properties.setProperty("javax.sip.AUTOMATIC_DIALOG_SUPPORT", "off");
try {
// Create SipStack object
sipStack = sipFactory.createSipStack(properties);
logger.info("sipStack = " + sipStack);
} catch (PeerUnavailableException e) {
// could not find
// gov.nist.jain.protocol.ip.sip.SipStackImpl
// in the classpath
e.printStackTrace();
System.err.println(e.getMessage());
if (e.getCause() != null)
e.getCause().printStackTrace();
System.exit(0);
}
try {
headerFactory = sipFactory.createHeaderFactory();
addressFactory = sipFactory.createAddressFactory();
messageFactory = sipFactory.createMessageFactory();
} catch (Exception ex) {
ex.printStackTrace();
}
}
public void init() {
try {
ListeningPoint lp = sipStack.createListeningPoint("127.0.0.1", 5065, "udp");
this.udpProvider = sipStack.createSipProvider(lp);
logger.info("udp provider " + udpProvider);
} catch (Exception ex) {
logger.error(ex.getMessage());
ex.printStackTrace();
usage();
}
}
public static void main(String args[]) throws Exception {
nonRFC3261Proxy = args.length > 0;
initFactories();
Forker f = new Forker();
f.init();
f.udpProvider.addSipListener(f);
}
public void processIOException(IOExceptionEvent exceptionEvent) {
// TODO Auto-generated method stub
}
public void processTransactionTerminated(
TransactionTerminatedEvent transactionTerminatedEvent) {
// TODO Auto-generated method stub
}
public void processDialogTerminated(
DialogTerminatedEvent dialogTerminatedEvent) {
// TODO Auto-generated method stub
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy