/* * 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.MaxForwardsHeader; import org.zoolu.sip.header.MultipleHeader; import org.zoolu.sip.header.RouteHeader; import org.zoolu.sip.header.RecordRouteHeader; import org.zoolu.sip.message.Message; import org.zoolu.sip.message.MessageFactory; import org.zoolu.sip.message.SipResponses; import org.zoolu.tools.LogLevel; //import java.util.Enumeration; import java.util.Vector; import java.io.BufferedReader; import java.io.InputStreamReader; /** Class Proxy implement a Proxy SIP Server. * It extends class Registrar. A Proxy can work as simply SIP Proxy, * or it can handle calls for registered users. */ public class Proxy extends Registrar { /** Log of processed calls */ CallLogger call_logger; /** Costructs a void Proxy */ protected Proxy() {} /** Costructs a new Proxy that acts also as location server for registered users. */ public Proxy(SipProvider provider, ServerProfile server_profile) { super(provider,server_profile); if (server_profile.call_log) call_logger=new CallLoggerImpl(SipStack.log_path+"//"+provider.getViaAddress()+"."+provider.getPort()+"_calls.log"); } /** When a new request is received for the local server. */ public void processRequestToLocalServer(Message msg) { printLog("inside processRequestToLocalServer(msg)",LogLevel.MEDIUM); if (msg.isRegister()) { super.processRequestToLocalServer(msg); } else if (!msg.isAck()) { // send a stateless error response //int result=501; // response code 501 ("Not Implemented") //int result=485; // response code 485 ("Ambiguous"); int result=484; // response code 484 ("Address Incomplete"); Message resp=MessageFactory.createResponse(msg,result,SipResponses.reasonOf(result),null); sip_provider.sendMessage(resp); } } /** When a new request message is received for a local user */ public void processRequestToLocalUser(Message msg) { printLog("inside processRequestToLocalUser(msg)",LogLevel.MEDIUM); if (server_profile.call_log) call_logger.update(msg); if (server_profile.do_proxy_authentication && !msg.isAck() && !msg.isCancel()) { // check message authentication Message err_resp=as.authenticateProxyRequest(msg); if (err_resp!=null) { sip_provider.sendMessage(err_resp); return; } } // message targets Vector targets=getTargets(msg); if (targets.isEmpty()) { // try to treat the request-URI as a local username or phone URL with a prefix-based nexthop rule 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()) sip_provider.sendMessage(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); sip_provider.sendMessage(request); } } /** When a new request message is received for a remote UA */ public void processRequestToRemoteUA(Message msg) { printLog("inside processRequestToRemoteUA(msg)",LogLevel.MEDIUM); if (call_logger!=null) call_logger.update(msg); 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); sip_provider.sendMessage(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) { sip_provider.sendMessage(err_resp); return; } } updateProxingRequest(msg); sip_provider.sendMessage(msg); } /** Processes the Proxy headers of the request. * Such headers are: Via, Record-Route, Route, Max-Forwards, etc. */ protected Message updateProxingRequest(Message msg) { printLog("inside updateProxingRequest(msg)",LogLevel.LOW); // remove Route if present boolean is_on_route=false; if (msg.hasRouteHeader()) { MultipleHeader mr=msg.getRoutes(); SipURL route=(new RouteHeader(mr.getTop())).getNameAddress().getAddress(); if (isResponsibleFor(route.getHost(),route.getPort())) { mr.removeTop(); if (mr.size()>0) msg.setRoutes(mr); else msg.removeRoutes(); is_on_route=true; } } // add Record-Route? if (server_profile.on_route && msg.isInvite() && !is_on_route) { SipURL rr_url; if (sip_provider.getPort()==SipStack.default_port) rr_url=new SipURL(sip_provider.getViaAddress()); else rr_url=new SipURL(sip_provider.getViaAddress(),sip_provider.getPort()); if (server_profile.loose_route) rr_url.addLr(); RecordRouteHeader rrh=new RecordRouteHeader(new NameAddress(rr_url)); msg.addRecordRouteHeader(rrh); } // which protocol? String proto=null; if (msg.hasRouteHeader()) { SipURL route=msg.getRouteHeader().getNameAddress().getAddress(); if (route.hasTransport()) proto=route.getTransport(); } else proto=msg.getRequestLine().getAddress().getTransport(); if (proto==null) proto=sip_provider.getDefaultTransport(); // add Via ViaHeader via=new ViaHeader(proto,sip_provider.getViaAddress(),sip_provider.getPort()); if (sip_provider.isRportSet()) via.setRport(); String branch=sip_provider.pickBranch(msg); if (server_profile.loop_detection) { String loop_tag=msg.getHeader(Loop_Tag).getValue(); if (loop_tag!=null) { msg.removeHeader(Loop_Tag); branch+=loop_tag; } } via.setBranch(branch); msg.addViaHeader(via); // decrement Max-Forwards MaxForwardsHeader maxfwd=msg.getMaxForwardsHeader(); if (maxfwd!=null) maxfwd.decrement(); else maxfwd=new MaxForwardsHeader(SipStack.max_forwards); msg.setMaxForwardsHeader(maxfwd); // domain name routing if (server_profile.domain_routing_rules!=null && server_profile.domain_routing_rules.length>0) { RequestLine rl=msg.getRequestLine(); SipURL request_uri=rl.getAddress(); for (int i=0; i<server_profile.domain_routing_rules.length; i++) { RoutingRule rule=(RoutingRule)server_profile.domain_routing_rules[i]; SipURL nexthop=rule.getNexthop(request_uri); if (nexthop!=null) { printLog("domain-based routing: "+rule.toString()+": YES",LogLevel.MEDIUM); printLog("target="+nexthop.toString(),LogLevel.MEDIUM); rl=new RequestLine(rl.getMethod(),nexthop); msg.setRequestLine(rl); break; } else printLog("prefix-based routing: "+rule.toString()+": NO",LogLevel.MEDIUM); } } // check whether the next Route is formed according to RFC2543 msg.rfc2543RouteAdapt(); return msg; } /** When a new response message is received */ public void processResponse(Message resp) { printLog("inside processResponse(msg)",LogLevel.MEDIUM); if(call_logger!=null) call_logger.update(resp); updateProxingResponse(resp); if (resp.hasViaHeader()) sip_provider.sendMessage(resp); else printLog("no VIA header found: message discarded",LogLevel.HIGH); } /** Processes the Proxy headers of the response. * Such headers are: Via, .. */ protected Message updateProxingResponse(Message resp) { printLog("inside updateProxingResponse(resp)",LogLevel.MEDIUM); ViaHeader vh=new ViaHeader((Header)resp.getVias().getHeaders().elementAt(0)); if (vh.getHost().equals(sip_provider.getViaAddress())) resp.removeViaHeader(); return resp; } /** Tries to find the target for a username or phone URL not registered within the location service. */ protected SipURL getPhoneTarget(SipURL request_uri) { String username=request_uri.getUserName(); if (username!=null && isPhoneNumber(username)) { printLog(username+" is a phone number",LogLevel.MEDIUM); for (int i=0; i<server_profile.phone_routing_rules.length; i++) { RoutingRule rule=(RoutingRule)server_profile.phone_routing_rules[i]; SipURL nexthop=rule.getNexthop(request_uri); if (nexthop!=null) { printLog("prefix-based routing: "+rule.toString()+": YES",LogLevel.MEDIUM); printLog("target="+nexthop.toString(),LogLevel.MEDIUM); return nexthop; } else printLog("prefix-based routing: "+rule.toString()+": NO",LogLevel.MEDIUM); } } return null; } /** Whether the String is a phone number. */ protected boolean isPhoneNumber(String str) { if (str==null || str.length()==0) return false; for (int i=0; i<str.length(); i++) { char c=str.charAt(i); if (c!='+' && c!='-' && (c<'0' || c>'9')) return false; } return true; } // ****************************** Logs ***************************** /** Adds a new string to the default Log */ private void printLog(String str, int level) { if (log!=null) log.println("Proxy: "+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 Proxy [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); new Proxy(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) {} } }