/* * Copyright (C) 2005 Luca Veltri - University of Parma - Italy * * This source code is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This source code 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this source code; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author(s): * Luca Veltri (luca.veltri@unipr.it) */ package local.server; import org.zoolu.sip.address.*; import org.zoolu.sip.provider.*; import org.zoolu.sip.header.RequestLine; import org.zoolu.sip.header.Header; import org.zoolu.sip.header.ViaHeader; import org.zoolu.sip.header.MultipleHeader; import org.zoolu.sip.header.RouteHeader; import org.zoolu.sip.header.RecordRouteHeader; import org.zoolu.sip.transaction.*; import org.zoolu.sip.message.*; import org.zoolu.tools.LogLevel; import java.util.Vector; import java.util.Iterator; import java.util.HashSet; import java.io.BufferedReader; import java.io.InputStreamReader; /** StatefulProxy server. * Class StatefulProxy implement a stateful SIP proxy server. * It extends class Registrar. A StatefulProxy can work as simply SIP proxy, * or it can handle calls for registered users. */ public class StatefulProxy extends Proxy implements TransactionClientListener { /** Transactions state */ protected StatefulProxyState state=null; /** end timeout server transactions ("Timer C" in RFC 3261) */ //Timer end_to; /** SipProvider for client transactions */ protected SipProvider sip_provider_client; /** SipProvider for server transactions */ protected SipProvider sip_provider_server; /** Costructs a void StatefulProxy */ protected StatefulProxy() {} /** Inits the stateful server */ private void init() { sip_provider_client=sip_provider; sip_provider_server=sip_provider; state=new StatefulProxyState(); } /** Costructs a new StatefulProxy that acts also as location server for registered users. */ /*public StatefulProxy(SipProvider provider_server, SipProvider provider_client, ServerProfile server_profile) { super(provider_server,server_profile); sip_provider_client=provider_client; sip_provider_server=provider_server; init(); }*/ /** Costructs a new StatefulProxy that acts also as location server for registered users. */ public StatefulProxy(SipProvider provider, ServerProfile server_profile) { super(provider,server_profile); init(); } /** When a new request is received for the local server */ public void processRequestToLocalServer(Message req) { printLog("inside processRequestToLocalServer(msg)",LogLevel.MEDIUM); super.processRequestToLocalServer(req); } /** When a new request message is received for a local user */ public void processRequestToLocalUser(Message msg) { printLog("inside processRequestToLocalUser(msg)",LogLevel.MEDIUM); if (msg.isAck()) { printLog("ACK received out of an active InviteServerTransaction, message forwarded",LogLevel.MEDIUM); // just send the ack.. super.processRequestToLocalUser(msg); return; } TransactionServer ts; if (msg.isInvite()) ts=new InviteTransactionServer(sip_provider_server,msg,null); else ts=new TransactionServer(sip_provider_server,msg,null); //ts.listen(); if (server_profile.do_proxy_authentication && !msg.isAck() && !msg.isCancel()) { // check message authentication Message err_resp=as.authenticateProxyRequest(msg); if (err_resp!=null) { ts.respondWith(err_resp); return; } } // message targets Vector targets=getTargets(msg); if (targets.isEmpty()) { // try to treat the request-URI as a phone URL SipURL request_uri=msg.getRequestLine().getAddress(); SipURL new_target=getPhoneTarget(request_uri); if (new_target!=null) targets.addElement(new_target.toString()); } if (targets.isEmpty()) { printLog("No target found, message discarded",LogLevel.HIGH); if (!msg.isAck()) statefulServerResponse(ts,MessageFactory.createResponse(msg,404,SipResponses.reasonOf(404),null)); return; } printLog("message will be forwarded to all user's contacts",LogLevel.MEDIUM); for (int i=0; i<targets.size(); i++) { SipURL url=new SipURL((String)(targets.elementAt(i))); Message request=new Message(msg); request.removeRequestLine(); request.setRequestLine(new RequestLine(msg.getRequestLine().getMethod(),url)); updateProxingRequest(request); TransactionClient tc; if (msg.isInvite()) tc=new InviteTransactionClient(sip_provider_client,request,this); else tc=new TransactionClient(sip_provider_client,request,this); //printLog("DEBUG: processLocalRequest()\r\n"+tc.getRequestMessage().toString(),LogLevel.LOWER); state.addClient(ts,tc); } HashSet clients=state.getClients(ts); for (Iterator i=clients.iterator(); i.hasNext(); ) ((TransactionClient)i.next()).request(); } /** When a new request message is received for a remote UA */ public void processRequestToRemoteUA(Message msg) { printLog("inside processRequestToRemoteUA(msg)",LogLevel.MEDIUM); if (msg.isAck()) { printLog("ACK received out of an active InviteServerTransaction, message forwarded",LogLevel.MEDIUM); // just send the ack.. super.processRequestToRemoteUA(msg); return; } TransactionServer ts; if (msg.isInvite()) ts=new InviteTransactionServer(sip_provider_server,msg,null); else ts=new TransactionServer(sip_provider_server,msg,null); //ts.listen(); if (!server_profile.is_open_proxy) { // check whether the caller is a local user SipURL from_url=msg.getFromHeader().getNameAddress().getAddress(); String from_username=from_url.getUserName(); String from_hostaddr=from_url.getHost(); String caller=(from_username==null)? from_hostaddr : from_username+"@"+from_hostaddr; if (!location_service.hasUser(caller)) { // but do not filter messages directed to local users SipURL to_url=msg.getToHeader().getNameAddress().getAddress(); String to_username=to_url.getUserName(); String to_hostaddr=to_url.getHost(); String callee=(to_username==null)? to_hostaddr : to_username+"@"+to_hostaddr; if (!location_service.hasUser(callee)) { // both caller and callee are not registered with the local server printLog("both users "+caller+" and "+callee+" are not registered with the local server: proxy denied.",LogLevel.HIGH); ts.respondWith(MessageFactory.createResponse(msg,503,SipResponses.reasonOf(503),null)); return; } } } if (server_profile.do_proxy_authentication && !msg.isAck() && !msg.isCancel()) { // check message authentication Message err_resp=as.authenticateProxyRequest(msg); if (err_resp!=null) { ts.respondWith(err_resp); return; } } updateProxingRequest(msg); TransactionClient tc; if (msg.isInvite()) tc=new InviteTransactionClient(sip_provider_client,msg,this); else tc=new TransactionClient(sip_provider_client,msg,this); state.addClient(ts,tc); tc.request(); } /** When a new response message is received */ public void processResponse(Message resp) { printLog("inside processResponse(msg)",LogLevel.MEDIUM); //printLog("Response received out of an active ClientTransaction, message discarded",LogLevel.HIGH); super.processResponse(resp); } /** Sends a server final response */ protected void statefulServerResponse(TransactionServer ts, Message resp) { printLog("inside statefulServerResponse(msg)",LogLevel.MEDIUM); printLog("Server response: "+resp.getStatusLine().toString(),LogLevel.MEDIUM); ts.respondWith(resp); } /** Process provisional response */ protected void processProvisionalResponse(Transaction transaction, Message resp) { printLog("inside processProvisionalResponse(t,resp)",LogLevel.MEDIUM); int code=resp.getStatusLine().getCode(); TransactionServer ts=state.getServer(transaction); if (ts!=null && code!=100) { updateProxingResponse(resp); if (resp.hasViaHeader()) ts.respondWith(resp); } } /** Process failure response */ protected void processFailureResponse(Transaction transaction, Message resp) { printLog("inside processFailureResponse(t,resp)",LogLevel.MEDIUM); TransactionServer ts=state.getServer(transaction); state.removeClient(transaction); if (ts==null) return; if (!state.hasServer(ts)) return; // updates the non-2xx final response state.setFinalResponse(ts,resp); // if there are no more pending clients, sends the final response HashSet clients=state.getClients(ts); if (clients.isEmpty()) { printLog("only this tr_client remained: send the response",LogLevel.LOW); resp=state.getFinalResponse(ts); updateProxingResponse(resp); if (resp.hasViaHeader()) ts.respondWith(resp); state.removeServer(ts); } } /** Process success response */ protected void processSuccessResponse(Transaction transaction, Message resp) { printLog("inside processSuccessResponse(t,resp)",LogLevel.MEDIUM); TransactionServer ts=state.getServer(transaction); state.removeClient(transaction); if (ts==null) return; updateProxingResponse(resp); if (resp.hasViaHeader()) { ts.respondWith(resp); if (!state.hasServer(ts)) return; //else // cancel all other pending transaction clients HashSet clients=state.getClients(ts); //printLog("Cancel pending clients..",LogLevel.LOW); //if (clients==null) return; printLog("Cancelling "+clients.size()+" pending clients",LogLevel.LOW); for (Iterator i=clients.iterator(); i.hasNext(); ) { Transaction tc=(Transaction)i.next(); Message cancel=MessageFactory.createCancelRequest(tc.getRequestMessage()); TransactionClient tr_cancel=new TransactionClient(sip_provider_server,cancel,null); tr_cancel.request(); } state.removeServer(ts); } } /** Process tmeout */ protected void processTimeout(Transaction transaction) { printLog("inside processTimeout(t)",LogLevel.MEDIUM); TransactionServer ts=state.getServer(transaction); state.removeClient(transaction); if (ts==null) return; HashSet clients=(HashSet)state.getClients(ts); if (clients==null) return; if (clients.isEmpty()) { printLog("DEBUG: responding..",LogLevel.LOW); //printLog("DEBUG:\r\n"+state.getFinalResponse(ts),LogLevel.LOW); Message resp=state.getFinalResponse(ts); updateProxingResponse(resp); if (resp.hasViaHeader()) statefulServerResponse(ts,resp); state.removeServer(ts); } } // ******************* TransactionClient callback methods ******************* /** When the TransactionClient is in "Proceeding" state and receives a new 1xx response */ public void onTransProvisionalResponse(TransactionClient transaction, Message resp) { processProvisionalResponse(transaction,resp); } /** When the TransactionClient goes into the "Completed" state, receiving a failure response */ public void onTransFailureResponse(TransactionClient transaction, Message resp) { processFailureResponse(transaction,resp); } /** When an TransactionClient goes into the "Terminated" state, receiving a 2xx response */ public void onTransSuccessResponse(TransactionClient transaction, Message resp) { processSuccessResponse(transaction,resp); } /** When the TransactionClient goes into the "Terminated" state, caused by transaction timeout */ public void onTransTimeout(TransactionClient transaction) { processTimeout(transaction); } // ****************************** Logs ***************************** /** Adds a new string to the default Log */ private void printLog(String str, int level) { if (log!=null) log.println("StatefulProxy: "+str,level+SipStack.LOG_LEVEL_UA); } // ****************************** MAIN ***************************** /** The main method. */ public static void main(String[] args) { String file=null; boolean prompt_exit=false; for (int i=0; i<args.length; i++) { if (args[i].equals("-f") && args.length>(i+1)) { file=args[++i]; continue; } if (args[i].equals("--prompt")) { prompt_exit=true; continue; } if (args[i].equals("-h")) { System.out.println("usage:\n java StatefulProxy [options] \n"); System.out.println(" options:"); System.out.println(" -h this help"); System.out.println(" -f <config_file> specifies a configuration file"); System.out.println(" --prompt prompt for exit"); System.exit(0); } } SipStack.init(file); SipProvider sip_provider=new SipProvider(file); ServerProfile server_profile=new ServerProfile(file); StatefulProxy sproxy=new StatefulProxy(sip_provider,server_profile); // promt before exit if (prompt_exit) try { System.out.println("press 'enter' to exit"); BufferedReader in=new BufferedReader(new InputStreamReader(System.in)); in.readLine(); System.exit(0); } catch (Exception e) {} } }