/* * Copyright (C) 2009 Risto Känsäkoski - Sesca ISW Ltd * Copyright (C) 2005 Luca Veltri - University of Parma - Italy * * This file is part of SIP-Applet (www.sesca.com, www.purplescout.com) * This file is modified from MjSip (http://www.mjsip.org) * * MjSip 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. * * MjSip 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 MjSip; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ package org.zoolu.sip.provider; import org.zoolu.net.*; import org.zoolu.sip.header.*; import org.zoolu.sip.message.Message; import org.zoolu.sip.address.*; import org.zoolu.tools.Configure; import org.zoolu.tools.Configurable; import org.zoolu.tools.Parser; import org.zoolu.tools.Random; import org.zoolu.tools.Log; import org.zoolu.tools.LogLevel; import org.zoolu.tools.RotatingLog; // import org.zoolu.tools.MD5; import org.zoolu.tools.SimpleDigest; import org.zoolu.tools.DateFormat; import java.util.Hashtable; import java.io.IOException; import java.net.UnknownHostException; // PersonalJava // import java.util.HashSet; // import java.util.Iterator; import org.zoolu.tools.HashSet; import org.zoolu.tools.Iterator; import com.sesca.misc.Logger; import com.sesca.voip.transport.HttpTunnelServer; import com.sesca.voip.transport.HttpTunnelServerListener; import com.sesca.voip.transport.HttpTunnelSocket; import com.sesca.voip.transport.HttpTunnelTransport; import com.sesca.voip.ua.UserAgentProfile; import java.util.Enumeration; import java.util.Date; import local.net.RtpPacket; /** * SipProvider implements the SIP transport layer, that is the layer responsable * for sending and receiving SIP messages. Messages are received by the callback * function defined in the interface SipProviderListener. * <p> * SipProvider implements also multiplexing/demultiplexing service through the * use of SIP interface identifiers and <i>onReceivedMessage()<i/> callback * function of specific SipProviderListener. * <p> * A SipProviderListener can be added to a SipProvider through the * addSipProviderListener(id,listener) method, where: <b> - <i>id<i/> is the * SIP interface identifier the listener has to be bound to, <b> - <i>listener<i/> * is the SipProviderListener that received messages are passed to. <p/> The SIP * interface identifier specifies the type of messages the listener is going to * receive for. Together with the specific SipProvider, it represents the * complete SIP Service Access Point (SAP) address/identifier used for * demultiplexing SIP messages at receiving side. <p/> The identifier can be of * one of the three following types: transaction_id, dialog_id, or method_id. * These types of identifiers characterize respectively: <br> - messages within * a specific transaction, <br> - messages within a specific dialog, <br> - * messages related to a specific SIP method. It is also possible to use the the * identifier ANY to specify <br> - all messages that are out of any * transactions, dialogs, or already specified method types. * <p> * When receiving a message, the SipProvider first tries to look for a matching * transaction, then looks for a matching dialog, then for a matching method * type, and finally for a default listener (i.e. that with identifier ANY). For * the matched SipProviderListener, the method <i>onReceivedMessage()</i> is * fired. * <p> * Note: no 482 (Loop Detected) responses are generated for requests that does * not properly match any ongoing transactions, dialogs, nor method types. */ public class SipProvider implements Configurable, TransportListener, TcpServerListener, HttpTunnelServerListener { // **************************** Constants **************************** /** UDP protocol type */ public static final String PROTO_UDP = "udp"; /** TCP protocol type */ public static final String PROTO_TCP = "tcp"; /** TLS protocol type */ public static final String PROTO_TLS = "tls"; /** SCTP protocol type */ public static final String PROTO_SCTP = "sctp"; public static final String PROTO_HTTP = "http"; public String UABase = SipStack.ua_info; public String UAIdentity = null; /** * String value "auto-configuration" used for auto configuration of the host * address. */ public static final String AUTO_CONFIGURATION = "AUTO-CONFIGURATION"; /** * String value "auto-configuration" used for auto configuration of the host * address. */ public static final String ALL_INTERFACES = "ALL-INTERFACES"; /** String value "NO-OUTBOUND" used for setting no outbound proxy. */ // public static final String NO_OUTBOUND="NO-OUTBOUND"; /** * Identifier used as listener id for capturing ANY incoming messages that * does not match any active method_id, transaction_id, nor dialog_id. <br> * In this context, "active" means that there is a active listener for that * specific method, transaction, or dialog. */ public static final Identifier ANY = new Identifier("ANY"); /** * Identifier used as listener id for capturing any incoming messages in * PROMISQUE mode, that means that messages are passed to the present * listener regardless of any other active SipProviderListeners for specific * messages. <p/> More than one SipProviderListener can be added and be * active concurrently for capturing messages in PROMISQUE mode. */ public static final Identifier PROMISQUE = new Identifier("PROMISQUE"); public static int stat_rport_num = 0; /** Minimum length for a valid SIP message. */ private static final int MIN_MESSAGE_LENGTH = 12; // ***************** Readable/configurable attributes ***************** /** * Via address/name. Use 'auto-configuration' for auto detection, or let it * undefined. */ String via_addr = null; /** Local SIP port */ int host_port = 0; int rport_num = 0; public boolean halted=false; /** * Network interface (IP address) used by SIP. Use 'ALL-INTERFACES' for * binding SIP to all interfaces (or let it undefined). */ String host_ifaddr = null; /** Transport protocols (the first protocol is used as default) */ String[] transport_protocols = null; /** Max number of (contemporary) open connections */ int nmax_connections = 0; /** * Outbound proxy (host_addr[:host_port]). Use 'NONE' for not using an * outbound proxy (or let it undefined). */ SocketAddress outbound_proxy = null; /** Whether logging all packets (including non-SIP keepalive tokens). */ boolean log_all_packets = false; // for backward compatibility: /** Outbound proxy addr (for backward compatibility). */ private String outbound_addr = null; /** Outbound proxy port (for backward compatibility). */ private int outbound_port = -1; // ********************* Non-readable attributes ********************* /** Event Loger */ protected Log event_log = null; /** Message Loger */ protected Log message_log = null; /** Network interface (IP address) used by SIP. */ IpAddress host_ipaddr = null; /** Default transport */ String default_transport = null; /** Whether using UDP as transport protocol */ boolean transport_udp = false; /** Whether using TCP as transport protocol */ boolean transport_tcp = false; /** Whether using TLS as transport protocol */ boolean transport_tls = false; /** Whether using SCTP as transport protocol */ boolean transport_sctp = false; boolean transport_http = false; /** Whether adding 'rport' parameter on outgoing requests. */ boolean rport = true; /** * Whether forcing 'rport' parameter on incoming requests ('force-rport' * mode). */ boolean force_rport = false; /** List of provider listeners */ Hashtable listeners = null; /** List of exception listeners */ HashSet exception_listeners = null; /** UDP transport */ UdpTransport udp = null; /** Tcp server */ TcpServer tcp_server = null; HttpTunnelServer http_server = null; /** Connections */ Hashtable connections = null; private UserAgentProfile user_profile; // *************************** Costructors *************************** /** Creates a void SipProvider. */ /* * protected SipProvider() { } */ /** Creates a new SipProvider. */ public SipProvider(String via_addr, int port) { init(via_addr, port, null, null); initlog(); startTrasport(); } /** * Creates a new SipProvider. Costructs the SipProvider, initializing the * SipProviderListeners, the transport protocols, and other attributes. */ public SipProvider(String via_addr, int port, String[] protocols, String ifaddr) { init(via_addr, port, protocols, ifaddr); initlog(); startTrasport(); } public SipProvider(String via_addr, int port, String[] protocols, String ifaddr, String outbound_addr, UserAgentProfile uap) { // port=80; // setOutboundProxy(new SocketAddress(outbound_proxy, port)); this.user_profile=uap; this.outbound_addr = outbound_addr; init(via_addr, port, protocols, ifaddr); initlog(); startTrasport(); } //adds a new constructor to handle NAT / STUN public SipProvider(String via_addr,int rport, int port, String[] protocols, String ifaddr, String outbound_addr, UserAgentProfile uap) { // port=80; // setOutboundProxy(new SocketAddress(outbound_proxy, port)); this.user_profile=uap; this.outbound_addr = outbound_addr; this.rport_num = rport; stat_rport_num = rport; init(via_addr, port, protocols, ifaddr); initlog(); startTrasport(); } /** * Creates a new SipProvider. The SipProvider attributres are read from file. */ public SipProvider(String file) { if(!SipStack.isInit()) SipStack.init(file); new Configure(this, file); init(via_addr, host_port, transport_protocols, host_ifaddr); initlog(); startTrasport(); } /** * Inits the SipProvider, initializing the SipProviderListeners, the * transport protocols, the outbound proxy, and other attributes. */ private void init(String viaddr, int port, String[] protocols, String ifaddr) { if(!SipStack.isInit()) SipStack.init(); via_addr = viaddr; if(via_addr == null || via_addr.equalsIgnoreCase(AUTO_CONFIGURATION)) via_addr = IpAddress.getLocalHostAddress().toString(); host_port = port; // host_port=80; if(host_port <= 0) host_port = SipStack.default_port; host_ipaddr = null; if(ifaddr != null && !ifaddr.equalsIgnoreCase(ALL_INTERFACES)) { try { host_ipaddr = IpAddress.getByName(ifaddr); } catch (IOException e) { e.printStackTrace(); host_ipaddr = null; } } transport_protocols = protocols; if(transport_protocols == null) transport_protocols = SipStack.default_transport_protocols; for (int i = 0; i < transport_protocols.length; i++) { transport_protocols[i] = transport_protocols[i].toLowerCase(); if(transport_protocols[i].equals(PROTO_UDP)) transport_udp = true; else if(transport_protocols[i].equals(PROTO_TCP)) transport_tcp = true; else if(transport_protocols[i].equals(PROTO_HTTP)) { transport_http = true; transport_protocols[i] = PROTO_UDP; } /* * else if (transport_protocols[i].equals(PROTO_TLS)) * transport_tls=true; else if * (transport_protocols[i].equals(PROTO_SCTP)) transport_sctp=true; */ } default_transport = transport_protocols[0]; if(nmax_connections <= 0) nmax_connections = SipStack.default_nmax_connections; // just for backward compatibility.. if(outbound_port < 0) outbound_port = SipStack.default_port; if(outbound_addr != null) { if(outbound_addr.equalsIgnoreCase(Configure.NONE) || outbound_addr.equalsIgnoreCase("NO-OUTBOUND")) outbound_proxy = null; else outbound_proxy = new SocketAddress(outbound_addr, outbound_port); } rport = SipStack.use_rport; force_rport = SipStack.force_rport; exception_listeners = new HashSet(); listeners = new Hashtable(); connections = new Hashtable(); } /** Inits logs. */ private void initlog() { if(SipStack.debug_level > 0) { String filename = SipStack.log_path + "//" + via_addr + "." + host_port; event_log = new RotatingLog(filename + "_events.log", null, SipStack.debug_level, SipStack.max_logsize * 1024, SipStack.log_rotations, SipStack.rotation_scale, SipStack.rotation_time); message_log = new RotatingLog(filename + "_messages.log", null, SipStack.debug_level, SipStack.max_logsize * 1024, SipStack.log_rotations, SipStack.rotation_scale, SipStack.rotation_time); } printLog("Date: " + DateFormat.formatHHMMSS(new Date()), LogLevel.HIGH); printLog("SipStack: " + SipStack.release, LogLevel.HIGH); printLog("new SipProvider(): " + toString(), LogLevel.HIGH); } /** Starts the transport services. */ private void startTrasport() { // start udp if(transport_udp) { try { if(host_ipaddr == null) udp = new UdpTransport(host_port, this); else udp = new UdpTransport(host_port, host_ipaddr, this); printLog("udp is up", LogLevel.MEDIUM); } catch (Exception e) { printException(e, LogLevel.HIGH); } } // start tcp if(transport_tcp) { Logger.info("SipProvider.startTransport: tcp"); try { if(host_ipaddr == null) tcp_server = new TcpServer(host_port, this); else tcp_server = new TcpServer(host_port, host_ipaddr, this); printLog("tcp is up", LogLevel.MEDIUM); } catch (Exception e) { printException(e, LogLevel.HIGH); e.printStackTrace(); } } // start http tunnel if(transport_http) { Logger.info("SipProvider.startTransport: http tunnel"); try { // http_server=new HttpTunnelServer(conf.tunnelPort,new // IpAddress(conf.tunnelServer),this); // http_server=new HttpTunnelServer(conf.tunnelPort,this); } catch (Exception e) { printException(e, LogLevel.HIGH); e.printStackTrace(); } ConnectionIdentifier conn_id = new ConnectionIdentifier("tcp", new IpAddress(user_profile.tunnelServer), user_profile.tunnelPort); if(!connections.containsKey(conn_id)) { // printLog("no active connection found matching // "+conn_id,LogLevel.MEDIUM); // printLog("open "+proto+" connection to // "+dest_ipaddr+":"+dest_port,LogLevel.MEDIUM); Logger.info("SipProvider: no active connection found matching " + conn_id); Logger.info("SipProvider: open tcp connection to tunnel server"); HttpTunnelTransport conn = null; try { conn = new HttpTunnelTransport(new IpAddress(user_profile.tunnelServer), user_profile.tunnelPort, this); Logger.debug("conn=" + conn); } catch (Exception e) { printLog("connection setup FAILED", LogLevel.HIGH); Logger.warning("connection setup FAILED"); e.printStackTrace(); } printLog("connection " + conn + " opened", LogLevel.HIGH); Logger.info("connection " + conn + " opened"); Logger.info("Adding connection: " + conn); addConnection(conn); Logger.debug(connections); } else { printLog("active connection found matching " + conn_id, LogLevel.MEDIUM); Logger.info("active connection found matching " + conn_id); } HttpTunnelTransport conn = (HttpTunnelTransport) connections.get(conn_id); Logger.debug("Connection is now: " + conn); if(conn != null) { printLog("sending data through conn " + conn, LogLevel.MEDIUM); Logger.info("sending data through conn " + conn); try { Logger.info("Trying to open tunnel..."); Message msg = new Message("GET / HTTP/1.1\r\nUdpHost: " + IpAddress.getByName(outbound_addr) + ":" + outbound_port + "\r\n\r\n"); // conn.sendMessage(msg); conn.sendMessage(msg.toString().getBytes(),true); Logger.info("Tunnel opened succesfully"); } catch (IOException e) { printException(e, LogLevel.HIGH); Logger.warning("Tunnel failed"); } } else { // this point has not to be reached printLog("ERROR: conn " + conn_id + " not found: abort.", LogLevel.MEDIUM); Logger.warning("ERROR: conn " + conn_id + " not found: abort."); } printLog("tunnel is up", LogLevel.MEDIUM); Logger.info("startTransport exit"); } // printLog("transport is up",LogLevel.MEDIUM); } /** Stops the transport services. */ private void stopTrasport() { // stop udp if(udp != null) { printLog("udp is going down", LogLevel.LOWER); udp.halt(); udp = null; } // stop tcp if(tcp_server != null) { printLog("tcp is going down", LogLevel.LOWER); tcp_server.halt(); tcp_server = null; } if(connections != null) { printLog("connections are going down", LogLevel.LOWER); for (Enumeration e = connections.elements(); e.hasMoreElements();) { ConnectedTransport c = (ConnectedTransport) e.nextElement(); c.halt(); } connections = null; } } /** Stops the SipProviders. */ public void halt() { halted=true; printLog("halt: SipProvider is going down", LogLevel.MEDIUM); stopTrasport(); listeners = new Hashtable(); exception_listeners = new HashSet(); } public void release_port() { if(udp != null) { udp.release_port(); } if(tcp_server != null) {} if(connections != null) {} } /** Parses a single line (loaded from the config file) */ public void parseLine(String line) { String attribute; Parser par; int index = line.indexOf("="); if(index > 0) { attribute = line.substring(0, index).trim(); par = new Parser(line, index + 1); } else { attribute = line; par = new Parser(""); } char[] delim = {' ', ','}; if(attribute.equals("via_addr")) { via_addr = par.getString(); return; } if(attribute.equals("host_port")) { host_port = par.getInt(); return; } if(attribute.equals("host_ifaddr")) { host_ifaddr = par.getString(); return; } if(attribute.equals("transport_protocols")) { transport_protocols = par.getWordArray(delim); return; } if(attribute.equals("nmax_connections")) { nmax_connections = par.getInt(); return; } if(attribute.equals("outbound_proxy")) { String soaddr = par.getString(); if(soaddr == null || soaddr.length() == 0 || soaddr.equalsIgnoreCase(Configure.NONE) || soaddr.equalsIgnoreCase("NO-OUTBOUND")) outbound_proxy = null; else outbound_proxy = new SocketAddress(soaddr); return; } if(attribute.equals("log_all_packets")) { log_all_packets = (par.getString().toLowerCase().startsWith("y")); return; } // old parameters if(attribute.equals("host_addr")) System.err.println("WARNING: parameter 'host_addr' is no more supported; use 'via_addr' instead."); if(attribute.equals("all_interfaces")) System.err.println("WARNING: parameter 'all_interfaces' is no more supported; use 'host_iaddr' for setting a specific interface or let it undefined."); if(attribute.equals("use_outbound")) System.err.println("WARNING: parameter 'use_outbound' is no more supported; use 'outbound_proxy' for setting an outbound proxy or let it undefined."); if(attribute.equals("outbound_addr")) { System.err.println("WARNING: parameter 'outbound_addr' has been deprecated; use 'outbound_proxy=<host_addr>[:<host_port>]' instead."); outbound_addr = par.getString(); return; } if(attribute.equals("outbound_port")) { System.err.println("WARNING: parameter 'outbound_port' has been deprecated; use 'outbound_proxy=<host_addr>[:<host_port>]' instead."); outbound_port = par.getInt(); return; } } /** Converts the entire object into lines (to be saved into the config file) */ protected String toLines() { // currently not implemented.. return toString(); } /** Gets a String with the list of transport protocols. */ private String transportProtocolsToString() { String list = transport_protocols[0]; for (int i = 1; i < transport_protocols.length; i++) list += "/" + transport_protocols[i]; return list; } // ************************** Public methods ************************* /** Gets via address. */ public String getViaAddress() { return via_addr; } /** Sets via address. */ /* * public void setViaAddress(String addr) { via_addr=addr; } */ /** Gets host port. */ public int getPort() { return host_port; } public int getPortB() { if ( rport_num != 0 ) return rport_num; else return host_port; } /** * Whether binding the sip provider to all interfaces or only on the * specified host address. */ public boolean isAllInterfaces() { return host_ipaddr == null; } /** Gets host interface IpAddress. */ public IpAddress getInterfaceAddress() { return host_ipaddr; } /** Gets array of transport protocols. */ public String[] getTransportProtocols() { return transport_protocols; } /** Gets the default transport protocol. */ public String getDefaultTransport() { return default_transport; } /** Gets the default transport protocol. */ public void setDefaultTransport(String proto) { default_transport = proto; } /** Sets rport support. */ public void setRport(boolean flag) { rport = flag; } /** Whether using rport. */ public boolean isRportSet() { return rport; } /** Sets 'force-rport' mode. */ public void setForceRport(boolean flag) { force_rport = flag; } /** Whether using 'force-rport' mode. */ public boolean isForceRportSet() { return force_rport; } /** Whether has outbound proxy. */ public boolean hasOutboundProxy() { return outbound_proxy != null; } /** Gets the outbound proxy. */ public SocketAddress getOutboundProxy() { return outbound_proxy; } /** Sets the outbound proxy. Use 'null' for not using any outbound proxy. */ public void setOutboundProxy(SocketAddress soaddr) { outbound_proxy = soaddr; } /** Removes the outbound proxy. */ /* * public void removeOutboundProxy() { setOutboundProxy(null); } */ /** Gets the max number of (contemporary) open connections. */ public int getNMaxConnections() { return nmax_connections; } /** Sets the max number of (contemporary) open connections. */ public void setNMaxConnections(int n) { nmax_connections = n; } /** Gets event log. */ public Log getLog() { return event_log; } /** Returns the list (Hashtable) of active listener_IDs. */ public Hashtable getListeners() { return listeners; } /** * Adds a new listener to the SipProvider for caputering any message in * PROMISQUE mode. It is the same as using method * addSipProviderListener(SipProvider.PROMISQUE,listener). <p/> When * capturing messages in promisque mode all messages are passed to the * SipProviderListener before passing them to the specific listener (if * present). <br/> Note that more that one SipProviderListener can be active * in promisque mode at the same time;in that case the same message is passed * to all PROMISQUE SipProviderListeners. * * @param listener * is the SipProviderListener. * @return It returns <i>true</i> if the SipProviderListener is added, * <i>false</i> if the listener_ID is already in use. */ public boolean addSipProviderPromisqueListener(SipProviderListener listener) { return addSipProviderListener(PROMISQUE, listener); } /** * Adds a new listener to the SipProvider for caputering ANY message. It is * the same as using method addSipProviderListener(SipProvider.ANY,listener). * * @param listener * is the SipProviderListener. * @return It returns <i>true</i> if the SipProviderListener is added, * <i>false</i> if the listener_ID is already in use. */ public boolean addSipProviderListener(SipProviderListener listener) { return addSipProviderListener(ANY, listener); } /** * Adds a new listener to the SipProvider. * * @param id * is the unique identifier for the messages which the listener as * to be associated to. It is used as key. It can identify a * method, a transaction, or a dialog. Use SipProvider.ANY to * capture all messages. Use SipProvider.PROMISQUE if you want to * capture all message in promisque mode (letting other listeners * to capture the same received messages). * @param listener * is the SipProviderListener for this message id. * @return It returns <i>true</i> if the SipProviderListener is added, * <i>false</i> if the listener_ID is already in use. */ public boolean addSipProviderListener(Identifier id, SipProviderListener listener) { Logger.debug("adding SipProviderListener: " + id); boolean ret; Identifier key = id; if(listeners.containsKey(key)) { Logger.debug("trying to add a SipProviderListener with a id that is already in use"); ret = false; } else { listeners.put(key, listener); ret = true; Logger.debug("Added SipProviderListener: "+listener); } if(listeners != null) { String list = ""; for (Enumeration e = listeners.keys(); e.hasMoreElements();) list += e.nextElement() + ", "; printLog(listeners.size() + " listeners: " + list, LogLevel.LOW); } Logger.debug("SipProviderListeners:"); for (Enumeration e = listeners.keys(); e.hasMoreElements();) Logger.debug(" "+ e.nextElement()); Logger.debug(""); return ret; } /** * Removes a SipProviderListener. * * @param id * is the unique identifier used to select the listened messages. * @return It returns <i>true</i> if the SipProviderListener is removed, * <i>false</i> if the identifier is missed. */ public boolean removeSipProviderListener(Identifier id) { Logger.debug("removing SipProviderListener: " + id); boolean ret; Identifier key = id; if(!listeners.containsKey(key)) { Logger.debug("trying to remove a missed SipProviderListener."); ret = false; } else { listeners.remove(key); ret = true; } if(listeners != null) { String list = ""; for (Enumeration e = listeners.keys(); e.hasMoreElements();) list += e.nextElement() + ", "; printLog(listeners.size() + " listeners: " + list, LogLevel.LOW); } Logger.debug("SipProviderListeners:"); for (Enumeration e = listeners.keys(); e.hasMoreElements();) Logger.debug(" "+ e.nextElement()); Logger.debug(""); return ret; } /** * Sets the SipProviderExceptionListener. The SipProviderExceptionListener is * the listener for all exceptions thrown by the SipProviders. * * @param e_listener * is the SipProviderExceptionListener. * @return It returns <i>true</i> if the SipProviderListener has been * correctly set, <i>false</i> if the SipProviderListener was * already set. */ public boolean addSipProviderExceptionListener(SipProviderExceptionListener e_listener) { printLog("adding SipProviderExceptionListener", LogLevel.MEDIUM); if(exception_listeners.contains(e_listener)) { printWarning("trying to add an already present SipProviderExceptionListener.", LogLevel.HIGH); return false; } else { exception_listeners.add(e_listener); return true; } } /** * Removes a SipProviderExceptionListener. * * @param e_listener * is the SipProviderExceptionListener. * @return It returns <i>true</i> if the SipProviderExceptionListener has * been correctly removed, <i>false</i> if the * SipProviderExceptionListener is missed. */ public boolean removeSipProviderExceptionListener(SipProviderExceptionListener e_listener) { printLog("removing SipProviderExceptionListener", LogLevel.MEDIUM); if(!exception_listeners.contains(e_listener)) { printWarning("trying to remove a missed SipProviderExceptionListener.", LogLevel.HIGH); return false; } else { exception_listeners.remove(e_listener); return true; } } /** * Sends a Message, specifing the transport portocol, nexthop address and * port. * <p> * This is a low level method and forces the message to be routed to a * specific nexthop address, port and transport, regardless whatever the Via, * Route, or request-uri, address to. * <p> * In case of connection-oriented transport, the connection is selected as * follows: <br> - if an existing connection is found matching the * destination end point (socket), such connection is used, otherwise <br> - * a new connection is established * * @return It returns a Connection in case of connection-oriented delivery * (e.g. TCP) or null in case of connection-less delivery (e.g. UDP) */ public ConnectionIdentifier sendMessage(Message msg, String proto, String dest_addr, int dest_port, int ttl) { if(log_all_packets || msg.getLength() > MIN_MESSAGE_LENGTH) printLog("Resolving host address '" + dest_addr + "'", LogLevel.MEDIUM); try { IpAddress dest_ipaddr = IpAddress.getByName(dest_addr); return sendMessage(msg, proto, dest_ipaddr, dest_port, ttl); } catch (Exception e) { printException(e, LogLevel.HIGH); return null; } } /** * Sends a Message, specifing the transport portocol, nexthop address and * port. */ private ConnectionIdentifier sendMessage(Message msg, String proto, IpAddress dest_ipaddr, int dest_port, int ttl) { if (halted) return null; Logger.debug("SipProvider.sendMessage(Message msg, String proto, IpAddress dest_ipaddr, int dest_port, int ttl)"); Logger.debug(msg.toString()); ConnectionIdentifier conn_id = new ConnectionIdentifier(proto, dest_ipaddr, dest_port); if(transport_http) { Logger.debug("transport_http"); try { conn_id = new ConnectionIdentifier("tcp", IpAddress.getByName(user_profile.tunnelServer), user_profile.tunnelPort); } catch (UnknownHostException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } if(log_all_packets || msg.getLength() > MIN_MESSAGE_LENGTH) printLog("Sending message to " + conn_id, LogLevel.MEDIUM); if(transport_udp && proto.equals(PROTO_UDP)) { // UDP Logger.debug("transport_udp"); // printLog("using UDP",LogLevel.LOW); conn_id = null; try { // if (ttl>0 && multicast_address) do something? udp.sendMessage(msg, dest_ipaddr, dest_port); } catch (IOException e) { printException(e, LogLevel.HIGH); return null; } } else if(transport_tcp && proto.equals(PROTO_TCP)) { // TCP Logger.debug("transport_tcp"); // printLog("using TCP",LogLevel.LOW); if(!connections.containsKey(conn_id)) { printLog("no active connection found matching " + conn_id, LogLevel.MEDIUM); printLog("open " + proto + " connection to " + dest_ipaddr + ":" + dest_port, LogLevel.MEDIUM); Logger.info("SipProvider (tcp): no active connection found matching " + conn_id); Logger.info("SipProvider (tcp): open " + proto + " connection to " + dest_ipaddr + ":" + dest_port); TcpTransport conn = null; try { conn = new TcpTransport(dest_ipaddr, dest_port, this); } catch (Exception e) { Logger.info("connection setup FAILED"); return null; } Logger.info("connection " + conn + " opened"); addConnection(conn); } else { Logger.info("active connection found matching " + conn_id); } ConnectedTransport conn = (ConnectedTransport) connections.get(conn_id); if(conn != null) { Logger.info("sending data through conn " + conn); try { conn.sendMessage(msg); conn_id = new ConnectionIdentifier(conn); } catch (IOException e) { printException(e, LogLevel.HIGH); return null; } } else { // this point has not to be reached Logger.warning("ERROR: conn " + conn_id + " not found: abort."); return null; } } else if(transport_http) { Logger.debug("transport_http again"); Logger.hysteria(">>Sending message to HTTP tunnel"); // printLog("using TCP",LogLevel.LOW); if(!connections.containsKey(conn_id)) { printLog("no active connection found matching " + conn_id, LogLevel.MEDIUM); printLog("open " + proto + " connection to " + dest_ipaddr + ":" + dest_port, LogLevel.MEDIUM); Logger.hysteria("SipProvider (http): no active connection found matching " + conn_id); Logger.hysteria("SipProvider (http): open tcp connection to " + user_profile.tunnelServer + ":" + user_profile.tunnelPort); HttpTunnelTransport conn = null; try { Logger.debug(user_profile.tunnelServer); Logger.debug(user_profile.tunnelPort+""); //conn = new HttpTunnelTransport(dest_ipaddr, dest_port, this); conn = new HttpTunnelTransport(new IpAddress(user_profile.tunnelServer), user_profile.tunnelPort, this); Logger.debug("conn="+conn); } catch (Exception e) { printLog("connection setup FAILED", LogLevel.HIGH); Logger.error("connection setup FAILED"); e.printStackTrace(); return null; } printLog("connection " + conn + " opened", LogLevel.HIGH); Logger.hysteria("connection " + conn + " opened"); Logger.hysteria("Adding connection: " + conn); addConnection(conn); Logger.debug(connections+""); } else { printLog("active connection found matching " + conn_id, LogLevel.MEDIUM); Logger.hysteria("active connection found matching " + conn_id); } HttpTunnelTransport conn = (HttpTunnelTransport) connections.get(conn_id); Logger.hysteria("Connection is now: " + conn); if(conn != null) { printLog("sending data through conn " + conn, LogLevel.MEDIUM); Logger.hysteria("sending data through conn " + conn); try { // Logger.info("Trying to re-open tunnel..."); // Message msg2 = new Message("GET / HTTP/1.1\r\nUdpHost: " + IpAddress.getByName(outbound_addr) + ":" + outbound_port + "\r\n\r\n"); // conn.sendMessage(msg); // conn.sendMessage(msg2.toString().getBytes()); // Logger.info("Tunnel re-opened succesfully"); try { conn.sendMessage(msg); conn_id = new ConnectionIdentifier(conn); } catch (IOException e) { printException(e, LogLevel.HIGH); return null; } } // catch (IOException e) catch (Exception e) { printException(e, LogLevel.HIGH); Logger.warning("Tunnel re-opening failed"); } } else { // this point has not to be reached printLog("ERROR: conn " + conn_id + " not found: abort.", LogLevel.MEDIUM); Logger.warning("ERROR: conn " + conn_id + " not found: abort."); return null; } } else { // otherwise printWarning("Unsupported protocol (" + proto + "): Message discarded", LogLevel.HIGH); Logger.error("Unsupported protocol (" + proto + "): Message discarded"); return null; } // logs String dest_addr = dest_ipaddr.toString(); printMessageLog(proto, dest_addr, dest_port, msg.getLength(), msg, "sent"); return conn_id; } /** * Sends the message <i>msg</i>. * <p> * The destination for the request is computed as follows: <br> - if * <i>outbound_addr</i> is set, <i>outbound_addr</i> and <i>outbound_port</i> * are used, otherwise <br> - if message has Route header with lr option * parameter (i.e. RFC3261 compliant), the first Route address is used, * otherwise <br> - the request's Request-URI is considered. * <p> * The destination for the response is computed based on the sent-by * parameter in the Via header field (RFC3261 compliant) * <p> * As transport it is used the protocol specified in the 'via' header field * <p> * In case of connection-oriented transport: <br> - if an already established * connection is found matching the destination end point (socket), such * connection is used, otherwise <br> - a new connection is established * * @return Returns a ConnectionIdentifier in case of connection-oriented * delivery (e.g. TCP) or null in case of connection-less delivery * (e.g. UDP) */ public ConnectionIdentifier sendMessage(Message msg) { if (halted) return null; printLog("Sending message:\r\n" + msg.toString(), LogLevel.LOWER); Logger.paranoia("Sending message:"); Logger.paranoia(msg.toString()); // select the transport protocol ViaHeader via = msg.getViaHeader(); String proto = via.getProtocol().toLowerCase(); printLog("using transport " + proto, LogLevel.MEDIUM); // select the destination address and port String dest_addr = null; int dest_port = 0; int ttl = 0; if(msg.isRequest()) { // REQUESTS if(outbound_proxy != null) { dest_addr = outbound_proxy.getAddress().toString(); dest_port = outbound_proxy.getPort(); } else { if(msg.hasRouteHeader() && msg.getRouteHeader().getNameAddress().getAddress().hasLr()) { SipURL url = msg.getRouteHeader().getNameAddress().getAddress(); dest_addr = url.getHost(); dest_port = url.getPort(); } else { SipURL url = msg.getRequestLine().getAddress(); dest_addr = url.getHost(); dest_port = url.getPort(); if(url.hasMaddr()) { dest_addr = url.getMaddr(); if(url.hasTtl()) ttl = url.getTtl(); // update the via header by adding maddr and ttl params via.setMaddr(dest_addr); if(ttl > 0) via.setTtl(ttl); msg.removeViaHeader(); msg.addViaHeader(via); } } } } else { // RESPONSES SipURL url = via.getSipURL(); if(via.hasReceived()) dest_addr = via.getReceived(); else dest_addr = url.getHost(); if(via.hasRport()) dest_port = via.getRport(); if(dest_port <= 0) dest_port = url.getPort(); } if(dest_port <= 0) dest_port = SipStack.default_port; return sendMessage(msg, proto, dest_addr, dest_port, ttl); } /** Sends the message <i>msg</i> using the specified connection. */ public ConnectionIdentifier sendMessage(Message msg, ConnectionIdentifier conn_id) { Logger.debug("SipProvider.sendMessage: conn_id="+conn_id); if(log_all_packets || msg.getLength() > MIN_MESSAGE_LENGTH) printLog("Sending message through conn " + conn_id, LogLevel.HIGH); printLog("message:\r\n" + msg.toString(), LogLevel.LOWER); if(conn_id != null && connections.containsKey(conn_id)) { // connection exists printLog("active connection found matching " + conn_id, LogLevel.MEDIUM); ConnectedTransport conn = (ConnectedTransport) connections.get(conn_id); try { conn.sendMessage(msg); // logs // String proto=conn.getProtocol(); String proto = conn.getProtocol(); String dest_addr = conn.getRemoteAddress().toString(); int dest_port = conn.getRemotePort(); printMessageLog(proto, dest_addr, dest_port, msg.getLength(), msg, "sent"); return conn_id; } catch (Exception e) { printException(e, LogLevel.HIGH); } } // else printLog("no active connection found matching " + conn_id, LogLevel.MEDIUM); return sendMessage(msg); } /** * Processes the message received. It is called each time a new message is * received by the transport layer, and it performs the actual message * processing. */ protected void processReceivedMessage(Message msg) { try { // logs Logger.debug("SipProvider.processReceivedMessage"); // discard too short messages Logger.paranoia(" Testing message length..."); if(msg.getLength() <= 2) { if(log_all_packets) Logger.info("message too short: discarded\r\n"); return; } Logger.paranoia(" Message length OK"); Logger.paranoia(" Is it a SIP message=?"); // discard non-SIP messages String first_line = msg.getFirstLine(); if(first_line == null || first_line.toUpperCase().indexOf("SIP/2.0") < 0) { Logger.info("Not a sip message"); if(log_all_packets) Logger.info("NOT a SIP message: discarded\r\n"); return; } Logger.paranoia(" It is"); Logger.debug("SipProvider.processReceivedMessage: Message accepted"); Logger.debug("message:\r\n" + msg.toString()); // if a request, handle "received" and "rport" parameters if(msg.isRequest()) { Logger.debug("Message is a request"); ViaHeader vh = msg.getViaHeader(); boolean via_changed = false; String src_addr = msg.getRemoteAddress(); int src_port = msg.getRemotePort(); String via_addr = vh.getHost(); int via_port = vh.getPort(); if(via_port<= 0) via_port = SipStack.default_port; if(!via_addr.equals(src_addr)) { vh.setReceived(src_addr); via_changed = true; } if(vh.hasRport()) { vh.setRport(src_port); via_changed = true; } else { if(force_rport && via_port != src_port) { vh.setRport(src_port); via_changed = true; } } if(via_changed) { msg.removeViaHeader(); msg.addViaHeader(vh); } } // is there any listeners? if(listeners == null || listeners.size() == 0) { Logger.debug("no listener found: meesage discarded."); return; } // try to look for a UA in promisque mode if(listeners.containsKey(PROMISQUE)) { Logger.debug("message passed to uas: " + PROMISQUE+" (PROMISQUE)"); ((SipProviderListener) listeners.get(PROMISQUE)).onReceivedMessage(this, msg); } // after the callback check if the message is still valid if(!msg.isRequest() && !msg.isResponse()) { Logger.debug("No valid SIP message: message discarded."); return; } // this was the promisque listener; now keep on looking for a tighter // listener.. // try to look for a transaction Identifier key = msg.getTransactionId(); Logger.debug("DEBUG: transaction-id: " + key); if(listeners.containsKey(key)) { Logger.debug("message passed to transaction: " + key); Logger.debug(" transaction="+listeners.get(key).toString()+"("+listeners.get(key).getClass()+")"); ((SipProviderListener) listeners.get(key)).onReceivedMessage(this, msg); return; } // try to look for a dialog Logger.paranoia("Looking for a dialog..."); key = msg.getDialogId(); Logger.debug("DEBUG: dialog-id: " + key); if(listeners.containsKey(key)) { Logger.debug("message passed to dialog: " + key); Logger.debug(" dialog="+listeners.get(key).toString()+"("+listeners.get(key).getClass()+")"); ((SipProviderListener) listeners.get(key)).onReceivedMessage(this, msg); return; } // try to look for a UAS key = msg.getMethodId(); if(listeners.containsKey(key)) { Logger.debug("message passed to uas: " + key+" (key)"); Logger.debug(" uas="+listeners.get(key).toString()+"("+listeners.get(key).getClass()+")"); ((SipProviderListener) listeners.get(key)).onReceivedMessage(this, msg); return; } // try to look for a default UA if(listeners.containsKey(ANY)) { Logger.debug("message passed to uas: " + ANY+ "(ANY)"); ((SipProviderListener) listeners.get(ANY)).onReceivedMessage(this, msg); return; } // if we are here, no listener_ID matched.. Logger.debug("No SipListener found matching that message: message DISCARDED"); // printLog("Pending SipProviderListeners= "+getListeners().size(),3); Logger.debug("Pending SipProviderListeners= " + listeners.size()); for (Enumeration e = listeners.keys(); e.hasMoreElements();) Logger.debug(e.nextElement().toString()); } catch (Exception e) { Logger.error("Error handling a new incoming message [1]"); Logger.error("Message="+msg); if(exception_listeners == null || exception_listeners.size() == 0) { Logger.warning("Error handling a new incoming message [2]"); e.printStackTrace(); } else { for (Iterator i = exception_listeners.iterator(); i.hasNext();) try { ((SipProviderExceptionListener) i.next()).onMessageException(msg, e); } catch (Exception e2) { Logger.error("Error handling handling the Exception"); Logger.error(e2); } } } } /** Adds a new Connection */ private void addConnection(ConnectedTransport conn) { Logger.info("addConnection: conn=" + conn); ConnectionIdentifier conn_id = new ConnectionIdentifier(conn); Logger.info("addConnection: conn_id=" + conn_id); if(connections.containsKey(conn_id)) { // remove the previous connection printLog("trying to add the already established connection " + conn_id, LogLevel.HIGH); printLog("connection " + conn_id + " will be replaced", LogLevel.HIGH); Logger.info("trying to add the already established connection " + conn_id); Logger.info("connection " + conn_id + " will be replaced"); ConnectedTransport old_conn = (ConnectedTransport) connections.get(conn_id); old_conn.halt(); connections.remove(conn_id); } else if(connections.size() >= nmax_connections) { // remove the older unused connection printLog("reached the maximum number of connection: removing the older unused connection", LogLevel.HIGH); Logger.info("reached the maximum number of connection: removing the older unused connection"); long older_time = System.currentTimeMillis(); ConnectionIdentifier older_id = null; for (Enumeration e = connections.elements(); e.hasMoreElements();) { ConnectedTransport co = (ConnectedTransport) e.nextElement(); if(co.getLastTimeMillis() < older_time) older_id = new ConnectionIdentifier(co); } if(older_id != null) removeConnection(older_id); } connections.put(conn_id, conn); conn_id = new ConnectionIdentifier(conn); conn = (ConnectedTransport) connections.get(conn_id); // DEBUG log: printLog("active connenctions:", LogLevel.LOW); Logger.debug("active connenctions:"); for (Enumeration e = connections.keys(); e.hasMoreElements();) { ConnectionIdentifier id = (ConnectionIdentifier) e.nextElement(); printLog("conn-id=" + id + ": " + ((ConnectedTransport) connections.get(id)).toString(), LogLevel.LOW); Logger.debug("conn-id=" + id + ": " + ((ConnectedTransport) connections.get(id)).toString()); } } /** Removes a Connection */ private void removeConnection(ConnectionIdentifier conn_id) { Logger.info("removeConnection: " + conn_id); if(null != connections && connections.containsKey(conn_id)) { ConnectedTransport conn = (ConnectedTransport) connections.get(conn_id); conn.halt(); connections.remove(conn_id); // DEBUG log: printLog("active connenctions:", LogLevel.LOW); Logger.debug("active connenctions:"); for (Enumeration e = connections.elements(); e.hasMoreElements();) { ConnectedTransport co = (ConnectedTransport) e.nextElement(); printLog("conn " + co.toString(), LogLevel.LOW); Logger.debug("conn " + co.toString()); } } } // ************************* Callback methods ************************* /** When a new SIP message is received. */ public void onReceivedMessage(Transport transport, Message msg) { Logger.debug("SipProvider.onReceivedMessage"); Logger.debug("transport = "+transport); Logger.debug("Received message..."); processReceivedMessage(msg); } /** When Transport terminates. */ public void onTransportTerminated(Transport transport, Exception error) { printLog("transport " + transport + " terminated", LogLevel.MEDIUM); if(transport.getProtocol().equals(PROTO_TCP)) { ConnectionIdentifier conn_id = new ConnectionIdentifier((ConnectedTransport) transport); removeConnection(conn_id); } if(error != null) printException(error, LogLevel.HIGH); } /** When a new incoming Connection is established */ public void onIncomingConnection(TcpServer tcp_server, TcpSocket socket) { Logger.info("TcpServerListener awakes: onIncomingConnection"); // printLog("incoming connection from " + socket.getAddress() + ":" + socket.getPort(), LogLevel.MEDIUM); ConnectedTransport conn = new TcpTransport(socket, this); // printLog("tcp connection " + conn + " opened", LogLevel.MEDIUM); addConnection(conn); Logger.debug(conn+""); } /** When TcpServer terminates. */ public void onServerTerminated(TcpServer tcp_server, Exception error) { Logger.info("TcpServerListener awakes: onServerTerminated"); printLog("tcp server " + tcp_server + " terminated", LogLevel.MEDIUM); } // ************************** Other methods *************************** /** * Picks a fresh branch value. The branch ID MUST be unique across space and * time for all requests sent by the UA. The branch ID always begin with the * characters "z9hG4bK". These 7 characters are used by RFC 3261 as a magic * cookie. */ public static String pickBranch() { // String str=Long.toString(Math.abs(Random.nextLong()),16); // if (str.length()<5) str+="00000"; // return "z9hG4bK"+str.substring(0,5); return "z9hG4bK" + Random.nextNumString(5); } /** * Picks an unique branch value based on a SIP message. This value could also * be used as transaction ID */ public String pickBranch(Message msg) { StringBuffer sb = new StringBuffer(); sb.append(msg.getRequestLine().getAddress().toString()); sb.append(getViaAddress() + getPortB()); ViaHeader top_via = msg.getViaHeader(); if(top_via.hasBranch()) sb.append(top_via.getBranch()); else { sb.append(top_via.getHost() + top_via.getPort()); sb.append(msg.getCSeqHeader().getSequenceNumber()); sb.append(msg.getCallIdHeader().getCallId()); sb.append(msg.getFromHeader().getTag()); sb.append(msg.getToHeader().getTag()); } // return "z9hG4bK"+(new MD5(unique_str)).asHex().substring(0,9); return "z9hG4bK" + (new SimpleDigest(5, sb.toString())).asHex(); } /** * Picks a new tag. A tag MUST be globally unique and cryptographically * random with at least 32 bits of randomness. A property of this selection * requirement is that a UA will place a different tag into the From header * of an INVITE than it would place into the To header of the response to the * same INVITE. This is needed in order for a UA to invite itself to a * session. */ public static String pickTag() { // String str=Long.toString(Math.abs(Random.nextLong()),16); // if (str.length()<8) str+="00000000"; // return str.substring(0,8); return "z9hG4bK" + Random.nextNumString(8); } /** * Picks a new tag. The tag is generated uniquely based on message <i>req</i>. * This tag can be generated for responses in a stateless manner - in a * manner that will generate the same tag for the same request consistently. */ public static String pickTag(Message req) { // return String.valueOf(tag_generator++); // return (new MD5(request.toString())).asHex().substring(0,8); return (new SimpleDigest(8, req.toString())).asHex(); } /** * Picks a new call-id. The call-id is a globally unique identifier over * space and time. It is implemented in the form "localid@host". Call-id must * be considered case-sensitive and is compared byte-by-byte. */ public String pickCallId() { // String str=Long.toString(Math.abs(Random.nextLong()),16); // if (str.length()<12) str+="000000000000"; // return str.substring(0,12)+"@"+getViaAddress(); return Random.nextNumString(12) + "@" + getViaAddress(); } /** picks an initial CSeq */ public static int pickInitialCSeq() { return 1; } /** * (<b>Deprecated</b>) Constructs a NameAddress based on an input string. * The input string can be a: <br> - <i>user</i> name, <br> - * <i>user@address</i> url, <br> - <i>"Name" <sip:user@address></i> * address, * <p> * In the former case, a SIP URL is costructed using the outbound proxy as * host address if present, otherwise the local via address is used. */ public NameAddress completeNameAddress(String str) { if(str.indexOf("<sip:") >= 0) return new NameAddress(str); else { SipURL url = completeSipURL(str); return new NameAddress(url); } } /** Constructs a SipURL based on an input string. */ private SipURL completeSipURL(String str) { // in case it is passed only the 'user' field, add // '@'<outbound_proxy>[':'<outbound_port>] if(!str.startsWith("sip:") && str.indexOf("@") < 0 && str.indexOf(".") < 0 && str.indexOf(":") < 0) { // may be it is just the user name.. String url = "sip:" + str + "@"; if(outbound_proxy != null) { url += outbound_proxy.getAddress().toString(); int port = outbound_proxy.getPort(); if(port > 0 && port != SipStack.default_port) url += ":" + port; } else { url += via_addr; if(host_port > 0 && host_port != SipStack.default_port) url += ":" + host_port; } return new SipURL(url); } else return new SipURL(str); } /** * Constructs a SipURL for the given <i>username</i> on the local SIP UA. If * <i>username</i> is null, only host address and port are used. */ /* * public SipURL getSipURL(String user_name) { return new * SipURL(user_name,via_addr,(host_port!=SipStack.default_port)?host_port:-1); } */ // ******************************* Logs ******************************* /** Gets a String value for this object */ public String toString() { if(host_ipaddr == null) return host_port + "/" + transportProtocolsToString(); else return host_ipaddr.toString() + ":" + host_port + "/" + transportProtocolsToString(); } /** Adds a new string to the default Log */ private final void printLog(String str, int level) { if(event_log != null) { String provider_id = (host_ipaddr == null) ? Integer.toString(host_port) : host_ipaddr.toString() + ":" + host_port; event_log.println("SipProvider-" + provider_id + ": " + str, level + SipStack.LOG_LEVEL_TRANSPORT); } } /** Adds a WARNING to the default Log */ private final void printWarning(String str, int level) { printLog("WARNING: " + str, level); } /** Adds the Exception message to the default Log */ private final void printException(Exception e, int level) { if(event_log != null) event_log.printException(e, level + SipStack.LOG_LEVEL_TRANSPORT); } /** Adds the SIP message to the messageslog */ private final void printMessageLog(String proto, String addr, int port, int len, Message msg, String str) { if(log_all_packets || len >= MIN_MESSAGE_LENGTH) { if(message_log != null) { message_log.printPacketTimestamp(proto, addr, port, len, str + "\r\n" + msg.toString() + "-----End-of-message-----\r\n", 1); } if(event_log != null) { String first_line = msg.getFirstLine(); if(first_line != null) first_line = first_line.trim(); else first_line = "NOT a SIP message"; event_log.print("\r\n"); event_log.printPacketTimestamp(proto, addr, port, len, first_line + ", " + str, 1); event_log.print("\r\n"); } } } public void onIncomingConnection(HttpTunnelServer tcp_server, HttpTunnelSocket socket) { Logger.info("HttpTunnelServerListener(SipProvider) awakes: onIncomingConnection"); // printLog("incoming connection from " + socket.getAddress() + ":" + socket.getPort(), LogLevel.MEDIUM); ConnectedTransport conn = new HttpTunnelTransport(socket, this); // printLog("tcp connection " + conn + " opened", LogLevel.MEDIUM); addConnection(conn); Logger.debug(conn+""); } public void onServerTerminated(HttpTunnelServer tcp_server, Exception error) { Logger.info("HttpTunnelServerListener(SipProvider) awakes: onServerTerminated"); printLog("tcp server " + tcp_server + " terminated", LogLevel.MEDIUM); } public void onReceivedMessage(RtpPacket packet) { // TODO Auto-generated method stub } public boolean isTunneled() { return transport_http; } public void setIdentity(String identity) { UAIdentity=identity; } public UserAgentProfile getUserProfile() { return user_profile; } }