/** * Transaction constructor. * * @param newParentStack Parent stack for this transaction. * @param newEncapsulatedChannel Underlying channel for this transaction. */ protected SIPTransaction( SIPTransactionStack newParentStack, MessageChannel newEncapsulatedChannel) { sipStack = newParentStack; this.semaphore = new Semaphore(1, true); encapsulatedChannel = newEncapsulatedChannel; // Record this to check if the address has changed before sending // message to avoid possible race condition. this.peerPort = newEncapsulatedChannel.getPeerPort(); this.peerAddress = newEncapsulatedChannel.getPeerAddress(); this.peerInetAddress = newEncapsulatedChannel.getPeerInetAddress(); // @@@ hagai this.peerPacketSourcePort = newEncapsulatedChannel.getPeerPacketSourcePort(); this.peerPacketSourceAddress = newEncapsulatedChannel.getPeerPacketSourceAddress(); this.peerProtocol = newEncapsulatedChannel.getPeerProtocol(); if (this.isReliable()) { encapsulatedChannel.useCount++; if (sipStack.isLoggingEnabled(LogWriter.TRACE_DEBUG)) sipStack .getStackLogger() .logDebug( "use count for encapsulated channel" + this + " " + encapsulatedChannel.useCount); } this.currentState = null; disableRetransmissionTimer(); disableTimeoutTimer(); eventListeners = Collections.synchronizedSet(new HashSet<SIPTransactionEventListener>()); // Always add the parent stack as a listener // of this transaction addEventListener(newParentStack); }
/** * Changes the state of this transaction. * * @param newState New state of this transaction. */ public void setState(TransactionState newState) { // PATCH submitted by sribeyron if (currentState == TransactionState.COMPLETED) { if (newState != TransactionState.TERMINATED && newState != TransactionState.CONFIRMED) newState = TransactionState.COMPLETED; } if (currentState == TransactionState.CONFIRMED) { if (newState != TransactionState.TERMINATED) newState = TransactionState.CONFIRMED; } if (currentState != TransactionState.TERMINATED) currentState = newState; else newState = currentState; // END OF PATCH if (sipStack.isLoggingEnabled(LogWriter.TRACE_DEBUG)) { sipStack .getStackLogger() .logDebug( "Transaction:setState " + newState + " " + this + " branchID = " + this.getBranch() + " isClient = " + (this instanceof SIPClientTransaction)); sipStack.getStackLogger().logStackTrace(); } }
/** * Send a message to a specified receiver address. * * @param msg string to send. * @param peerAddress Address of the place to send it to. * @param peerPort the port to send it to. * @throws IOException If there is trouble sending this message. */ protected void sendMessage(byte[] msg, InetAddress peerAddress, int peerPort, boolean reConnect) throws IOException { // Via is not included in the request so silently drop the reply. if (sipStack.isLoggingEnabled() && this.sipStack.isLogStackTraceOnMessageSend()) { this.sipStack.getStackLogger().logStackTrace(StackLogger.TRACE_INFO); } if (peerPort == -1) { if (sipStack.isLoggingEnabled(LogWriter.TRACE_DEBUG)) { this.sipStack .getStackLogger() .logDebug(getClass().getName() + ":sendMessage: Dropping reply!"); } throw new IOException("Receiver port not set "); } else { if (sipStack.isLoggingEnabled(LogWriter.TRACE_DEBUG)) { this.sipStack .getStackLogger() .logDebug( "sendMessage " + peerAddress.getHostAddress() + "/" + peerPort + "\n" + "messageSize = " + msg.length + " message = " + new String(msg)); this.sipStack.getStackLogger().logDebug("*******************\n"); } } DatagramPacket reply = new DatagramPacket(msg, msg.length, peerAddress, peerPort); try { DatagramSocket sock; boolean created = false; if (sipStack.udpFlag) { // Use the socket from the message processor (for firewall // support use the same socket as the message processor // socket -- feature request # 18 from java.net). This also // makes the whole thing run faster! sock = ((UDPMessageProcessor) messageProcessor).sock; // Bind the socket to the stack address in case there // are multiple interfaces on the machine (feature reqeust // by Will Scullin) 0 binds to an ephemeral port. // sock = new DatagramSocket(0,sipStack.stackInetAddress); } else { // bind to any interface and port. sock = new DatagramSocket(); created = true; } sock.send(reply); if (created) sock.close(); } catch (IOException ex) { throw ex; } catch (Exception ex) { InternalErrorHandler.handleException(ex); } }
protected void runTask() { SIPTransaction transaction = SIPTransaction.this; // release the connection associated with this transaction. SIPTransactionStack sipStack = transaction.getSIPStack(); if (sipStack.isLoggingEnabled(LogWriter.TRACE_DEBUG)) { sipStack.getStackLogger().logDebug("LingerTimer: run() : " + getTransactionId()); } if (transaction instanceof SIPClientTransaction) { sipStack.removeTransaction(transaction); transaction.close(); } else if (transaction instanceof ServerTransaction) { // Remove it from the set if (sipStack.isLoggingEnabled(LogWriter.TRACE_DEBUG)) sipStack.getStackLogger().logDebug("removing" + transaction); sipStack.removeTransaction(transaction); if ((!sipStack.cacheServerConnections) && --transaction.encapsulatedChannel.useCount <= 0) { // Close the encapsulated socket if stack is configured transaction.close(); } else { if (sipStack.isLoggingEnabled(LogWriter.TRACE_DEBUG) && (!sipStack.cacheServerConnections) && transaction.isReliable()) { int useCount = transaction.encapsulatedChannel.useCount; sipStack.getStackLogger().logDebug("Use Count = " + useCount); } } } }
protected void semRelease() { try { if (sipStack.isLoggingEnabled(LogWriter.TRACE_DEBUG)) { sipStack.getStackLogger().logDebug("semRelease ]]]]" + this); sipStack.getStackLogger().logStackTrace(); } this.isSemaphoreAquired = false; this.semaphore.release(); } catch (Exception ex) { sipStack.getStackLogger().logError("Unexpected exception releasing sem", ex); } }
/** * Enables a timeout event to occur for this transaction after the number of ticks passed to this * method. * * @param tickCount Number of ticks before this transaction times out. */ protected final void enableTimeoutTimer(int tickCount) { if (sipStack.isLoggingEnabled(LogWriter.TRACE_DEBUG)) sipStack .getStackLogger() .logDebug( "enableTimeoutTimer " + this + " tickCount " + tickCount + " currentTickCount = " + timeoutTimerTicksLeft); timeoutTimerTicksLeft = tickCount; }
/** * Sets the request message that this transaction handles. * * @param newOriginalRequest Request being handled. */ public void setOriginalRequest(SIPRequest newOriginalRequest) { // Branch value of topmost Via header String newBranch; if (this.originalRequest != null && (!this.originalRequest .getTransactionId() .equals(newOriginalRequest.getTransactionId()))) { sipStack.removeTransactionHash(this); } // This will be cleared later. this.originalRequest = newOriginalRequest; // just cache the control information so the // original request can be released later. this.method = newOriginalRequest.getMethod(); this.from = (From) newOriginalRequest.getFrom(); this.to = (To) newOriginalRequest.getTo(); // Save these to avoid concurrent modification exceptions! this.toTag = this.to.getTag(); this.fromTag = this.from.getTag(); this.callId = (CallID) newOriginalRequest.getCallId(); this.cSeq = newOriginalRequest.getCSeq().getSeqNumber(); this.event = (Event) newOriginalRequest.getHeader("Event"); this.transactionId = newOriginalRequest.getTransactionId(); originalRequest.setTransaction(this); // If the message has an explicit branch value set, newBranch = ((Via) newOriginalRequest.getViaHeaders().getFirst()).getBranch(); if (newBranch != null) { if (sipStack.isLoggingEnabled(LogWriter.TRACE_DEBUG)) sipStack.getStackLogger().logDebug("Setting Branch id : " + newBranch); // Override the default branch with the one // set by the message setBranch(newBranch); } else { if (sipStack.isLoggingEnabled(LogWriter.TRACE_DEBUG)) sipStack .getStackLogger() .logDebug("Branch id is null - compute TID!" + newOriginalRequest.encode()); setBranch(newOriginalRequest.getTransactionId()); } }
public void cancelPingKeepAliveTimeoutTaskIfStarted() { if (pingKeepAliveTimeoutTask != null && pingKeepAliveTimeoutTask.getSipTimerTask() != null) { try { keepAliveSemaphore.acquire(); } catch (InterruptedException e) { logger.logError("Couldn't acquire keepAliveSemaphore"); return; } try { if (logger.isLoggingEnabled(LogWriter.TRACE_DEBUG)) { logger.logDebug( "~~~ cancelPingKeepAliveTimeoutTaskIfStarted for MessageChannel(key=" + key + "), clientAddress=" + peerAddress + ", clientPort=" + peerPort + ", timeout=" + keepAliveTimeout + ")"); } sipStack.getTimer().cancel(pingKeepAliveTimeoutTask); } finally { keepAliveSemaphore.release(); } } }
public ConnectionOrientedMessageChannel(SIPTransactionStack sipStack) { this.sipStack = sipStack; this.keepAliveTimeout = sipStack.getReliableConnectionKeepAliveTimeout(); if (keepAliveTimeout > 0) { keepAliveSemaphore = new Semaphore(1); } }
/** Run method specified by runnnable. */ public void run() { // Assume no thread pooling (bug fix by spierhj) ThreadAuditor.ThreadHandle threadHandle = null; while (true) { // Create a new string message parser to parse the list of messages. if (myParser == null) { myParser = new StringMsgParser(); myParser.setParseExceptionListener(this); } // messages that we write out to him. DatagramPacket packet; if (sipStack.threadPoolSize != -1) { synchronized (((UDPMessageProcessor) messageProcessor).messageQueue) { while (((UDPMessageProcessor) messageProcessor).messageQueue.isEmpty()) { // Check to see if we need to exit. if (!((UDPMessageProcessor) messageProcessor).isRunning) return; try { // We're part of a thread pool. Ask the auditor to // monitor this thread. if (threadHandle == null) { threadHandle = sipStack.getThreadAuditor().addCurrentThread(); } // Send a heartbeat to the thread auditor threadHandle.ping(); // Wait for packets // Note: getPingInterval returns 0 (infinite) if the // thread auditor is disabled. ((UDPMessageProcessor) messageProcessor) .messageQueue.wait(threadHandle.getPingIntervalInMillisecs()); } catch (InterruptedException ex) { if (!((UDPMessageProcessor) messageProcessor).isRunning) return; } } packet = (DatagramPacket) ((UDPMessageProcessor) messageProcessor).messageQueue.removeFirst(); } this.incomingPacket = packet; } else { packet = this.incomingPacket; } // Process the packet. Catch and log any exception we may throw. try { processIncomingDataPacket(packet); } catch (Exception e) { sipStack.logWriter.logError("Error while processing incoming UDP packet", e); } if (sipStack.threadPoolSize == -1) { return; } } }
/** * Return a reply from a pre-constructed reply. This sends the message back to the entity who * caused us to create this channel in the first place. * * @param sipMessage Message string to send. * @throws IOException If there is a problem with sending the message. */ public void sendMessage(SIPMessage sipMessage) throws IOException { if (sipStack.isLoggingEnabled() && this.sipStack.logStackTraceOnMessageSend) { if (sipMessage instanceof SIPRequest && ((SIPRequest) sipMessage).getRequestLine() != null) { /* * We dont want to log empty trace messages. */ this.sipStack.logWriter.logStackTrace(LogWriter.TRACE_MESSAGES); } else { this.sipStack.logWriter.logStackTrace(LogWriter.TRACE_MESSAGES); } } // Test and see where we are going to send the messsage. If the message // is sent back to oursleves, just // shortcircuit processing. long time = System.currentTimeMillis(); try { for (MessageProcessor messageProcessor : sipStack.getMessageProcessors()) { if (messageProcessor.getIpAddress().equals(this.peerAddress) && messageProcessor.getPort() == this.peerPort && messageProcessor.getTransport().equals(this.peerProtocol)) { MessageChannel messageChannel = messageProcessor.createMessageChannel(this.peerAddress, this.peerPort); if (messageChannel instanceof RawMessageChannel) { ((RawMessageChannel) messageChannel).processMessage(sipMessage); sipStack.logWriter.logDebug("Self routing message"); return; } } } byte[] msg = sipMessage.encodeAsBytes(this.getTransport()); sendMessage(msg, peerAddress, peerPort, peerProtocol, sipMessage instanceof SIPRequest); } catch (IOException ex) { throw ex; } catch (Exception ex) { sipStack.logWriter.logError("An exception occured while sending message", ex); throw new IOException("An exception occured while sending message"); } finally { if (sipStack.logWriter.isLoggingEnabled(ServerLog.TRACE_MESSAGES)) logMessage(sipMessage, peerAddress, peerPort, time); } }
/** Release the transaction semaphore. */ public void releaseSem() { try { this.toListener = false; this.semRelease(); } catch (Exception ex) { sipStack.getStackLogger().logError("Unexpected exception releasing sem", ex); } }
/** * Log a message into the log file. * * @param message message to log into the log file. */ private void logMessage(String message) { // String tname = Thread.currentThread().getName(); checkLogFile(); String logInfo = message; if (printWriter != null) { printWriter.println(logInfo); } if (sipStack.isLoggingEnabled()) { stackLogger.logInfo(logInfo); } }
/** * Constructor. We create one of these when we send out a message. * * @param targetAddr INET address of the place where we want to send messages. * @param port target port (where we want to send the message). * @param sipStack our SIP Stack. */ protected UDPMessageChannel( InetAddress targetAddr, int port, SIPTransactionStack sipStack, UDPMessageProcessor messageProcessor) { peerAddress = targetAddr; peerPort = port; peerProtocol = "UDP"; super.messageProcessor = messageProcessor; this.myAddress = messageProcessor.getIpAddress().getHostAddress(); this.myPort = messageProcessor.getPort(); this.sipStack = sipStack; // jeand : Create a new string message parser to parse the list of messages. myParser = sipStack.getMessageParserFactory().createMessageParser(sipStack); if (sipStack.isLoggingEnabled(LogWriter.TRACE_DEBUG)) { this.sipStack .getStackLogger() .logDebug("Creating message channel " + targetAddr.getHostAddress() + "/" + port); } }
/** * Implementation of the ParseExceptionListener interface. * * @param ex Exception that is given to us by the parser. * @throws ParseException If we choose to reject the header or message. */ public void handleException( ParseException ex, SIPMessage sipMessage, Class hdrClass, String header, String message) throws ParseException { if (sipStack.isLoggingEnabled()) this.sipStack.getStackLogger().logException(ex); // Log the bad message for later reference. if ((hdrClass != null) && (hdrClass.equals(From.class) || hdrClass.equals(To.class) || hdrClass.equals(CSeq.class) || hdrClass.equals(Via.class) || hdrClass.equals(CallID.class) || hdrClass.equals(RequestLine.class) || hdrClass.equals(StatusLine.class))) { if (sipStack.isLoggingEnabled()) { sipStack.getStackLogger().logError("BAD MESSAGE!"); sipStack.getStackLogger().logError(message); } throw ex; } else { sipMessage.addUnparsed(header); } }
/** * A given tx can process only a single outstanding event at a time. This semaphore gaurds * re-entrancy to the transaction. */ public boolean acquireSem() { boolean retval = false; try { if (sipStack.getStackLogger().isLoggingEnabled(LogWriter.TRACE_DEBUG)) { sipStack.getStackLogger().logDebug("acquireSem [[[[" + this); sipStack.getStackLogger().logStackTrace(); } if (this.sipStack.maxListenerResponseTime == -1) { this.semaphore.acquire(); retval = true; } else { retval = this.semaphore.tryAcquire(this.sipStack.maxListenerResponseTime, TimeUnit.SECONDS); } if (sipStack.isLoggingEnabled(LogWriter.TRACE_DEBUG)) sipStack.getStackLogger().logDebug("acquireSem() returning : " + retval); return retval; } catch (Exception ex) { sipStack.getStackLogger().logError("Unexpected exception acquiring sem", ex); InternalErrorHandler.handleException(ex); return false; } finally { this.isSemaphoreAquired = retval; } }
/** * Constructor. We create one of these when we send out a message. * * @param targetAddr INET address of the place where we want to send messages. * @param port target port (where we want to send the message). * @param sipStack our SIP Stack. */ protected UDPMessageChannel( InetAddress targetAddr, int port, SIPTransactionStack sipStack, UDPMessageProcessor messageProcessor) { peerAddress = targetAddr; peerPort = port; peerProtocol = "UDP"; super.messageProcessor = messageProcessor; this.myAddress = messageProcessor.getIpAddress().getHostAddress(); this.myPort = messageProcessor.getPort(); this.sipStack = sipStack; if (sipStack.isLoggingEnabled()) { this.sipStack.logWriter.logDebug( "Creating message channel " + targetAddr.getHostAddress() + "/" + port); } }
/** * Constructor. We create one of these in order to process an incoming message. * * @param stack is the SIP sipStack. * @param messageProcessor is the creating message processor. * @param packet is the incoming datagram packet. */ protected UDPMessageChannel( SIPTransactionStack stack, UDPMessageProcessor messageProcessor, DatagramPacket packet) { this.incomingPacket = packet; super.messageProcessor = messageProcessor; this.sipStack = stack; // jeand : Create a new string message parser to parse the list of messages. myParser = sipStack.getMessageParserFactory().createMessageParser(sipStack); this.myAddress = messageProcessor.getIpAddress().getHostAddress(); this.myPort = messageProcessor.getPort(); mythread = new Thread(this); mythread.setDaemon(true); mythread.start(); }
/** Constructor. */ public DefaultRouter(SipStack sipStack, String defaultRoute) { this.sipStack = (SipStackImpl) sipStack; if (defaultRoute != null) { try { this.defaultRoute = (Hop) this.sipStack .getAddressResolver() .resolveAddress((Hop) (new HopImpl(defaultRoute))); } catch (IllegalArgumentException ex) { // The outbound proxy is optional. If specified it should be host:port/transport. ((SIPTransactionStack) sipStack) .getStackLogger() .logError("Invalid default route specification - need host:port/transport"); throw ex; } } }
public SelectiveCharParser(SIPTransactionStack stack, Properties configurationProperties) { logger = stack.getStackLogger(); if (headersToParse.size() < 1) { boolean headersToParseFound = false; if (configurationProperties != null) { String headersToParseString = configurationProperties.getProperty(HEADERS_TO_PARSE); if (headersToParseString != null) { headersToParse.clear(); StringTokenizer stringTokenizer = new StringTokenizer(headersToParseString, ","); while (stringTokenizer.hasMoreTokens()) { String headerToParse = stringTokenizer.nextToken(); headersToParse.add(headerToParse.trim().toLowerCase()); } headersToParseFound = true; } } if (!headersToParseFound) { headersToParse.add(FromHeader.NAME.toLowerCase()); headersToParse.add(ToHeader.NAME.toLowerCase()); headersToParse.add(CSeqHeader.NAME.toLowerCase()); headersToParse.add(CallIdHeader.NAME.toLowerCase()); headersToParse.add(MaxForwardsHeader.NAME.toLowerCase()); headersToParse.add(ViaHeader.NAME.toLowerCase()); headersToParse.add(ContactHeader.NAME.toLowerCase()); headersToParse.add(RecordRouteHeader.NAME.toLowerCase()); headersToParse.add(RouteHeader.NAME.toLowerCase()); headersToParse.add(ContentLengthHeader.NAME.toLowerCase()); headersToParse.add(SubscriptionStateHeader.NAME.toLowerCase()); headersToParse.add(EventHeader.NAME.toLowerCase()); } if (logger.isLoggingEnabled()) { logger.logDebug("Headers to parse : "); for (String headerToParse : headersToParse) { logger.logDebug(headerToParse); } } } }
/** * Actually proces the parsed message. * * @param sipMessage */ public void processMessage(SIPMessage sipMessage) { if (sipMessage instanceof SIPRequest) { SIPRequest sipRequest = (SIPRequest) sipMessage; // This is a request - process it. // So far so good -- we will commit this message if // all processing is OK. if (sipStack.getStackLogger().isLoggingEnabled(ServerLogger.TRACE_MESSAGES)) { this.sipStack.serverLogger.logMessage( sipMessage, this.getPeerHostPort().toString(), this.getHost() + ":" + this.myPort, false, receptionTime); } final ServerRequestInterface sipServerRequest = sipStack.newSIPServerRequest(sipRequest, this); // Drop it if there is no request returned if (sipServerRequest == null) { if (sipStack.isLoggingEnabled()) { this.sipStack .getStackLogger() .logWarning("Null request interface returned -- dropping request"); } return; } if (sipStack.isLoggingEnabled(LogWriter.TRACE_DEBUG)) this.sipStack .getStackLogger() .logDebug("About to process " + sipRequest.getFirstLine() + "/" + sipServerRequest); try { sipServerRequest.processRequest(sipRequest, this); } finally { if (sipServerRequest instanceof SIPTransaction) { SIPServerTransaction sipServerTx = (SIPServerTransaction) sipServerRequest; if (!sipServerTx.passToListener()) { ((SIPTransaction) sipServerRequest).releaseSem(); } } } if (sipStack.isLoggingEnabled(LogWriter.TRACE_DEBUG)) this.sipStack .getStackLogger() .logDebug("Done processing " + sipRequest.getFirstLine() + "/" + sipServerRequest); // So far so good -- we will commit this message if // all processing is OK. } else { // Handle a SIP Reply message. SIPResponse sipResponse = (SIPResponse) sipMessage; try { sipResponse.checkHeaders(); } catch (ParseException ex) { if (sipStack.isLoggingEnabled()) sipStack .getStackLogger() .logError("Dropping Badly formatted response message >>> " + sipResponse); return; } ServerResponseInterface sipServerResponse = sipStack.newSIPServerResponse(sipResponse, this); if (sipServerResponse != null) { try { if (sipServerResponse instanceof SIPClientTransaction && !((SIPClientTransaction) sipServerResponse).checkFromTag(sipResponse)) { if (sipStack.isLoggingEnabled()) sipStack .getStackLogger() .logError("Dropping response message with invalid tag >>> " + sipResponse); return; } sipServerResponse.processResponse(sipResponse, this); } finally { if (sipServerResponse instanceof SIPTransaction && !((SIPTransaction) sipServerResponse).passToListener()) ((SIPTransaction) sipServerResponse).releaseSem(); } // Normal processing of message. } else { if (sipStack.isLoggingEnabled(LogWriter.TRACE_DEBUG)) { this.sipStack.getStackLogger().logDebug("null sipServerResponse!"); } } } }
/** * Process an incoming datagram * * @param packet is the incoming datagram packet. */ private void processIncomingDataPacket(DatagramPacket packet) throws Exception { this.peerAddress = packet.getAddress(); int packetLength = packet.getLength(); // Read bytes and put it in a eueue. byte[] bytes = packet.getData(); byte[] msgBytes = new byte[packetLength]; System.arraycopy(bytes, 0, msgBytes, 0, packetLength); // Do debug logging. if (sipStack.isLoggingEnabled(LogWriter.TRACE_DEBUG)) { this.sipStack .getStackLogger() .logDebug( "UDPMessageChannel: processIncomingDataPacket : peerAddress = " + peerAddress.getHostAddress() + "/" + packet.getPort() + " Length = " + packetLength); } SIPMessage sipMessage = null; try { this.receptionTime = System.currentTimeMillis(); sipMessage = myParser.parseSIPMessage(msgBytes, true, false, this); // myParser = null; } catch (ParseException ex) { // myParser = null; // let go of the parser reference. if (sipStack.isLoggingEnabled(LogWriter.TRACE_DEBUG)) { this.sipStack.getStackLogger().logDebug("Rejecting message ! " + new String(msgBytes)); this.sipStack.getStackLogger().logDebug("error message " + ex.getMessage()); this.sipStack.getStackLogger().logException(ex); } // JvB: send a 400 response for requests (except ACK) // Currently only UDP, @todo also other transports String msgString = new String(msgBytes, 0, packetLength); if (!msgString.startsWith("SIP/") && !msgString.startsWith("ACK ")) { String badReqRes = createBadReqRes(msgString, ex); if (badReqRes != null) { if (sipStack.isLoggingEnabled(LogWriter.TRACE_DEBUG)) { sipStack.getStackLogger().logDebug("Sending automatic 400 Bad Request:"); sipStack.getStackLogger().logDebug(badReqRes); } try { this.sendMessage(badReqRes.getBytes(), peerAddress, packet.getPort(), "UDP", false); } catch (IOException e) { this.sipStack.getStackLogger().logException(e); } } else { if (sipStack.isLoggingEnabled(LogWriter.TRACE_DEBUG)) { sipStack.getStackLogger().logDebug("Could not formulate automatic 400 Bad Request"); } } } return; } // No parse exception but null message - reject it and // march on (or return). // exit this message processor if the message did not parse. if (sipMessage == null) { if (sipStack.isLoggingEnabled(LogWriter.TRACE_DEBUG)) { this.sipStack.getStackLogger().logDebug("Rejecting message ! + Null message parsed."); } String key = packet.getAddress().getHostAddress() + ":" + packet.getPort(); if (pingBackRecord.get(key) == null && sipStack.getMinKeepAliveInterval() > 0) { byte[] retval = "\r\n\r\n".getBytes(); DatagramPacket keepalive = new DatagramPacket(retval, 0, retval.length, packet.getAddress(), packet.getPort()); PingBackTimerTask task = new PingBackTimerTask(packet.getAddress().getHostAddress(), packet.getPort()); this.pingBackRecord.put(key, task); this.sipStack.getTimer().schedule(task, sipStack.getMinKeepAliveInterval() * 1000); ((UDPMessageProcessor) this.messageProcessor).sock.send(keepalive); } else { sipStack.getStackLogger().logDebug("Not sending ping back"); } return; } Via topMostVia = sipMessage.getTopmostVia(); // Check for the required headers. if (sipMessage.getFrom() == null || sipMessage.getTo() == null || sipMessage.getCallId() == null || sipMessage.getCSeq() == null || topMostVia == null) { String badmsg = new String(msgBytes); if (sipStack.isLoggingEnabled()) { this.sipStack.getStackLogger().logError("bad message " + badmsg); this.sipStack .getStackLogger() .logError( ">>> Dropped Bad Msg " + "From = " + sipMessage.getFrom() + "To = " + sipMessage.getTo() + "CallId = " + sipMessage.getCallId() + "CSeq = " + sipMessage.getCSeq() + "Via = " + sipMessage.getViaHeaders()); } return; } // For a request first via header tells where the message // is coming from. // For response, just get the port from the packet. if (sipMessage instanceof SIPRequest) { Hop hop = sipStack.addressResolver.resolveAddress(topMostVia.getHop()); this.peerPort = hop.getPort(); this.peerProtocol = topMostVia.getTransport(); this.peerPacketSourceAddress = packet.getAddress(); this.peerPacketSourcePort = packet.getPort(); try { this.peerAddress = packet.getAddress(); // Check to see if the received parameter matches // the peer address and tag it appropriately. boolean hasRPort = topMostVia.hasParameter(Via.RPORT); if (hasRPort || !hop.getHost().equals(this.peerAddress.getHostAddress())) { topMostVia.setParameter(Via.RECEIVED, this.peerAddress.getHostAddress()); } if (hasRPort) { topMostVia.setParameter(Via.RPORT, Integer.toString(this.peerPacketSourcePort)); } } catch (java.text.ParseException ex1) { InternalErrorHandler.handleException(ex1); } } else { this.peerPacketSourceAddress = packet.getAddress(); this.peerPacketSourcePort = packet.getPort(); this.peerAddress = packet.getAddress(); this.peerPort = packet.getPort(); this.peerProtocol = topMostVia.getTransport(); } this.processMessage(sipMessage); }
/** * Send a message to a specified receiver address. * * @param msg message string to send. * @param peerAddress Address of the place to send it to. * @param peerPort the port to send it to. * @param peerProtocol protocol to use to send. * @throws IOException If there is trouble sending this message. */ protected void sendMessage( byte[] msg, InetAddress peerAddress, int peerPort, String peerProtocol, boolean retry) throws IOException { // Via is not included in the request so silently drop the reply. if (peerPort == -1) { if (sipStack.isLoggingEnabled(LogWriter.TRACE_DEBUG)) { this.sipStack .getStackLogger() .logDebug(getClass().getName() + ":sendMessage: Dropping reply!"); } throw new IOException("Receiver port not set "); } else { if (sipStack.isLoggingEnabled(LogWriter.TRACE_DEBUG)) { this.sipStack .getStackLogger() .logDebug( ":sendMessage " + peerAddress.getHostAddress() + "/" + peerPort + "\n" + " messageSize = " + msg.length); } } if (peerProtocol.compareToIgnoreCase("UDP") == 0) { DatagramPacket reply = new DatagramPacket(msg, msg.length, peerAddress, peerPort); try { DatagramSocket sock; if (sipStack.udpFlag) { sock = ((UDPMessageProcessor) messageProcessor).sock; } else { // bind to any interface and port. sock = sipStack.getNetworkLayer().createDatagramSocket(); } if (sipStack.isLoggingEnabled(LogWriter.TRACE_DEBUG)) { this.sipStack .getStackLogger() .logDebug( "sendMessage " + peerAddress.getHostAddress() + "/" + peerPort + "\n" + new String(msg)); } sock.send(reply); if (!sipStack.udpFlag) sock.close(); } catch (IOException ex) { throw ex; } catch (Exception ex) { InternalErrorHandler.handleException(ex); } } else { // Use TCP to talk back to the sender. Socket outputSocket = sipStack.ioHandler.sendBytes( this.messageProcessor.getIpAddress(), peerAddress, peerPort, "tcp", msg, retry, this); OutputStream myOutputStream = outputSocket.getOutputStream(); myOutputStream.write(msg, 0, msg.length); myOutputStream.flush(); // The socket is cached (dont close it!); } }
/** * Extract identities from certificates exchanged over TLS, based on guidelines from * draft-ietf-sip-domain-certs-04. * * @return list of authenticated identities */ public List<String> extractCertIdentities() throws SSLPeerUnverifiedException { if (this.getMessageChannel() instanceof TLSMessageChannel) { List<String> certIdentities = new ArrayList<String>(); Certificate[] certs = getPeerCertificates(); if (certs == null) { if (sipStack.isLoggingEnabled(LogWriter.TRACE_DEBUG)) { sipStack.getStackLogger().logDebug("No certificates available"); } return certIdentities; } for (Certificate cert : certs) { X509Certificate x509cert = (X509Certificate) cert; Collection<List<?>> subjAltNames = null; try { subjAltNames = x509cert.getSubjectAlternativeNames(); } catch (CertificateParsingException ex) { if (sipStack.isLoggingEnabled()) { sipStack.getStackLogger().logError("Error parsing TLS certificate", ex); } } // subjAltName types are defined in rfc2459 final Integer dnsNameType = 2; final Integer uriNameType = 6; if (subjAltNames != null) { if (sipStack.isLoggingEnabled(LogWriter.TRACE_DEBUG)) { sipStack.getStackLogger().logDebug("found subjAltNames: " + subjAltNames); } // First look for a URI in the subjectAltName field // as per draft-ietf-sip-domain-certs-04 for (List<?> altName : subjAltNames) { // 0th position is the alt name type // 1st position is the alt name data if (altName.get(0).equals(uriNameType)) { SipURI altNameUri; try { altNameUri = new AddressFactoryImpl().createSipURI((String) altName.get(1)); String altHostName = altNameUri.getHost(); if (sipStack.isLoggingEnabled(LogWriter.TRACE_DEBUG)) { sipStack .getStackLogger() .logDebug("found uri " + altName.get(1) + ", hostName " + altHostName); } certIdentities.add(altHostName); } catch (ParseException e) { if (sipStack.isLoggingEnabled()) { sipStack .getStackLogger() .logError("certificate contains invalid uri: " + altName.get(1)); } } } } // DNS An implementation MUST accept a domain name system // identifier as a SIP domain identity if and only if no other // identity is found that matches the "sip" URI type described // above. if (certIdentities.isEmpty()) { for (List<?> altName : subjAltNames) { if (altName.get(0).equals(dnsNameType)) { if (sipStack.isLoggingEnabled(LogWriter.TRACE_DEBUG)) { sipStack.getStackLogger().logDebug("found dns " + altName.get(1)); } certIdentities.add(altName.get(1).toString()); } } } } else { // If and only if the subjectAltName does not appear in the // certificate, the implementation MAY examine the CN field of the // certificate. If a valid DNS name is found there, the // implementation MAY accept this value as a SIP domain identity. String dname = x509cert.getSubjectDN().getName(); String cname = ""; try { Pattern EXTRACT_CN = Pattern.compile(".*CN\\s*=\\s*([\\w*\\.]+).*"); Matcher matcher = EXTRACT_CN.matcher(dname); if (matcher.matches()) { cname = matcher.group(1); if (sipStack.isLoggingEnabled(LogWriter.TRACE_DEBUG)) { sipStack.getStackLogger().logDebug("found CN: " + cname + " from DN: " + dname); } certIdentities.add(cname); } } catch (Exception ex) { if (sipStack.isLoggingEnabled()) { sipStack.getStackLogger().logError("exception while extracting CN", ex); } } } } return certIdentities; } else throw new UnsupportedOperationException("Not a TLS channel"); }
/** Set the passToListener flag to true. */ public void setPassToListener() { if (sipStack.isLoggingEnabled(LogWriter.TRACE_DEBUG)) { sipStack.getStackLogger().logDebug("setPassToListener()"); } this.toListener = true; }
/** * Gets invoked by the parser as a callback on successful message parsing (i.e. no parser errors). * * @param sipMessage Message to process (this calls the application for processing the message). * <p>Jvb: note that this code is identical to TCPMessageChannel, refactor some day */ public void processMessage(SIPMessage sipMessage) throws Exception { try { if (sipMessage.getFrom() == null || sipMessage.getTo() == null || sipMessage.getCallId() == null || sipMessage.getCSeq() == null || sipMessage.getViaHeaders() == null) { if (logger.isLoggingEnabled()) { String badmsg = sipMessage.encode(); logger.logError("bad message " + badmsg); logger.logError(">>> Dropped Bad Msg"); } return; } sipMessage.setRemoteAddress(this.peerAddress); sipMessage.setRemotePort(this.getPeerPort()); sipMessage.setLocalAddress(this.getMessageProcessor().getIpAddress()); sipMessage.setLocalPort(this.getPort()); // Issue 3: https://telestax.atlassian.net/browse/JSIP-3 sipMessage.setPeerPacketSourceAddress(this.peerAddress); sipMessage.setPeerPacketSourcePort(this.peerPort); ViaList viaList = sipMessage.getViaHeaders(); // For a request // first via header tells where the message is coming from. // For response, this has already been recorded in the outgoing // message. if (sipMessage instanceof SIPRequest) { Via v = (Via) viaList.getFirst(); // the peer address and tag it appropriately. Hop hop = sipStack.addressResolver.resolveAddress(v.getHop()); this.peerProtocol = v.getTransport(); // if(peerPortAdvertisedInHeaders <= 0) { int hopPort = v.getPort(); if (logger.isLoggingEnabled(LogWriter.TRACE_DEBUG)) { logger.logDebug( "hop port = " + hopPort + " for request " + sipMessage + " for this channel " + this + " key " + key); } if (hopPort <= 0) { // if port is 0 we assume the default port for TCP this.peerPortAdvertisedInHeaders = 5060; } else { this.peerPortAdvertisedInHeaders = hopPort; } if (logger.isLoggingEnabled(LogWriter.TRACE_DEBUG)) { logger.logDebug( "3.Storing peerPortAdvertisedInHeaders = " + peerPortAdvertisedInHeaders + " for this channel " + this + " key " + key); } // } // may be needed to reconnect, when diff than peer address if (peerAddressAdvertisedInHeaders == null) { peerAddressAdvertisedInHeaders = hop.getHost(); if (logger.isLoggingEnabled(LogWriter.TRACE_DEBUG)) { logger.logDebug( "3.Storing peerAddressAdvertisedInHeaders = " + peerAddressAdvertisedInHeaders + " for this channel " + this + " key " + key); } } try { if (mySock != null) { // selfrouting makes socket = null // https://jain-sip.dev.java.net/issues/show_bug.cgi?id=297 this.peerAddress = mySock.getInetAddress(); } // Check to see if the received parameter matches // the peer address and tag it appropriately. // JvB: dont do this. It is both costly and incorrect // Must set received also when it is a FQDN, regardless // whether // it resolves to the correct IP address // InetAddress sentByAddress = // InetAddress.getByName(hop.getHost()); // JvB: if sender added 'rport', must always set received boolean hasRPort = v.hasParameter(Via.RPORT); if (!hasRPort && v.getPort() != peerPort) { // https://github.com/RestComm/jain-sip/issues/79 if (logger.isLoggingEnabled(LogWriter.TRACE_DEBUG)) { logger.logDebug( "setting rport since viaPort " + v.getPort() + " different than peerPacketSourcePort " + peerPort + " so that the response can be routed back"); } hasRPort = true; } if (hasRPort || !hop.getHost().equals(this.peerAddress.getHostAddress())) { v.setParameter(Via.RECEIVED, this.peerAddress.getHostAddress()); } // @@@ hagai // JvB: technically, may only do this when Via already // contains // rport v.setParameter(Via.RPORT, Integer.toString(this.peerPort)); } catch (java.text.ParseException ex) { InternalErrorHandler.handleException(ex); } // Use this for outgoing messages as well. if (!this.isCached && mySock != null) { // self routing makes // mySock=null // https://jain-sip.dev.java.net/issues/show_bug.cgi?id=297 this.isCached = true; int remotePort = ((java.net.InetSocketAddress) mySock.getRemoteSocketAddress()).getPort(); String key = IOHandler.makeKey(mySock.getInetAddress(), remotePort); if (this.messageProcessor instanceof NioTcpMessageProcessor) { // https://java.net/jira/browse/JSIP-475 don't use iohandler in case of NIO // communications of the socket will leak in the iohandler sockettable ((NioTcpMessageProcessor) this.messageProcessor) .nioHandler.putSocket(key, mySock.getChannel()); } else { sipStack.ioHandler.putSocket(key, mySock); } // since it can close the socket it needs to be after the mySock usage otherwise // it the socket will be disconnected and NPE will be thrown in some edge cases ((ConnectionOrientedMessageProcessor) this.messageProcessor).cacheMessageChannel(this); } } // Foreach part of the request header, fetch it and process it long receptionTime = System.currentTimeMillis(); // if (sipMessage instanceof SIPRequest) { // This is a request - process the request. SIPRequest sipRequest = (SIPRequest) sipMessage; // Create a new sever side request processor for this // message and let it handle the rest. if (logger.isLoggingEnabled(LogWriter.TRACE_DEBUG)) { logger.logDebug("----Processing Message---"); } if (logger.isLoggingEnabled(ServerLogger.TRACE_MESSAGES)) { sipStack.serverLogger.logMessage( sipMessage, this.getPeerHostPort().toString(), this.messageProcessor.getIpAddress().getHostAddress() + ":" + this.messageProcessor.getPort(), false, receptionTime); } // Check for reasonable size - reject message // if it is too long. if (sipStack.getMaxMessageSize() > 0 && sipRequest.getSize() + (sipRequest.getContentLength() == null ? 0 : sipRequest.getContentLength().getContentLength()) > sipStack.getMaxMessageSize()) { SIPResponse sipResponse = sipRequest.createResponse(SIPResponse.MESSAGE_TOO_LARGE); byte[] resp = sipResponse.encodeAsBytes(this.getTransport()); this.sendMessage(resp, false); throw new Exception("Message size exceeded"); } String sipVersion = ((SIPRequest) sipMessage).getRequestLine().getSipVersion(); if (!sipVersion.equals("SIP/2.0")) { SIPResponse versionNotSupported = ((SIPRequest) sipMessage) .createResponse(Response.VERSION_NOT_SUPPORTED, "Bad SIP version " + sipVersion); this.sendMessage(versionNotSupported.encodeAsBytes(this.getTransport()), false); throw new Exception("Bad version "); } String method = ((SIPRequest) sipMessage).getMethod(); String cseqMethod = ((SIPRequest) sipMessage).getCSeqHeader().getMethod(); if (!method.equalsIgnoreCase(cseqMethod)) { SIPResponse sipResponse = sipRequest.createResponse(SIPResponse.BAD_REQUEST); byte[] resp = sipResponse.encodeAsBytes(this.getTransport()); this.sendMessage(resp, false); throw new Exception("Bad CSeq method" + sipMessage + " method " + method); } // Stack could not create a new server request interface. // maybe not enough resources. ServerRequestInterface sipServerRequest = sipStack.newSIPServerRequest(sipRequest, this); if (sipServerRequest != null) { try { sipServerRequest.processRequest(sipRequest, this); } finally { if (sipServerRequest instanceof SIPTransaction) { SIPServerTransaction sipServerTx = (SIPServerTransaction) sipServerRequest; if (!sipServerTx.passToListener()) ((SIPTransaction) sipServerRequest).releaseSem(); } } } else { if (sipStack.sipMessageValve == null) { // Allow message valves to nullify messages without error SIPResponse response = sipRequest.createResponse(Response.SERVICE_UNAVAILABLE); RetryAfter retryAfter = new RetryAfter(); // Be a good citizen and send a decent response code back. try { retryAfter.setRetryAfter((int) (10 * (Math.random()))); response.setHeader(retryAfter); this.sendMessage(response); } catch (Exception e) { // IGNore } if (logger.isLoggingEnabled()) logger.logWarning("Dropping message -- could not acquire semaphore"); } } } else { SIPResponse sipResponse = (SIPResponse) sipMessage; // JvB: dont do this // if (sipResponse.getStatusCode() == 100) // sipResponse.getTo().removeParameter("tag"); try { sipResponse.checkHeaders(); } catch (ParseException ex) { if (logger.isLoggingEnabled()) logger.logError("Dropping Badly formatted response message >>> " + sipResponse); return; } // This is a response message - process it. // Check the size of the response. // If it is too large dump it silently. if (sipStack.getMaxMessageSize() > 0 && sipResponse.getSize() + (sipResponse.getContentLength() == null ? 0 : sipResponse.getContentLength().getContentLength()) > sipStack.getMaxMessageSize()) { if (logger.isLoggingEnabled(LogWriter.TRACE_DEBUG)) logger.logDebug("Message size exceeded"); return; } ServerResponseInterface sipServerResponse = sipStack.newSIPServerResponse(sipResponse, this); if (sipServerResponse != null) { try { if (sipServerResponse instanceof SIPClientTransaction && !((SIPClientTransaction) sipServerResponse).checkFromTag(sipResponse)) { if (logger.isLoggingEnabled()) logger.logError("Dropping response message with invalid tag >>> " + sipResponse); return; } sipServerResponse.processResponse(sipResponse, this); } finally { if (sipServerResponse instanceof SIPTransaction && !((SIPTransaction) sipServerResponse).passToListener()) { // Note that the semaphore is released in event // scanner if the // request is actually processed by the Listener. ((SIPTransaction) sipServerResponse).releaseSem(); } } } else { logger.logWarning( "Application is blocked -- could not acquire semaphore -- dropping response"); } } } finally { } }
/** * Process the message through the transaction and sends it to the SIP peer. * * @param messageToSend Message to send to the SIP peer. */ public void sendMessage(final SIPMessage messageToSend) throws IOException { // Use the peer address, port and transport // that was specified when the transaction was // created. Bug was noted by Bruce Evangelder // soleo communications. try { final RawMessageChannel channel = (RawMessageChannel) encapsulatedChannel; for (MessageProcessor messageProcessor : sipStack.getMessageProcessors()) { boolean addrmatch = messageProcessor .getIpAddress() .getHostAddress() .toString() .equals(this.getPeerAddress()); if (addrmatch && messageProcessor.getPort() == this.getPeerPort() && messageProcessor.getTransport().equalsIgnoreCase(this.getPeerProtocol())) { if (channel instanceof TCPMessageChannel) { try { Runnable processMessageTask = new Runnable() { public void run() { try { ((TCPMessageChannel) channel) .processMessage( (SIPMessage) messageToSend.clone(), getPeerInetAddress()); } catch (Exception ex) { if (getSIPStack() .getStackLogger() .isLoggingEnabled(ServerLogger.TRACE_ERROR)) { getSIPStack() .getStackLogger() .logError("Error self routing message cause by: ", ex); } } } }; getSIPStack().getSelfRoutingThreadpoolExecutor().execute(processMessageTask); } catch (Exception e) { sipStack.getStackLogger().logError("Error passing message in self routing", e); } if (sipStack.isLoggingEnabled(LogWriter.TRACE_DEBUG)) sipStack.getStackLogger().logDebug("Self routing message"); return; } if (channel instanceof RawMessageChannel) { try { Runnable processMessageTask = new Runnable() { public void run() { try { ((RawMessageChannel) channel) .processMessage((SIPMessage) messageToSend.clone()); } catch (Exception ex) { if (getSIPStack() .getStackLogger() .isLoggingEnabled(ServerLogger.TRACE_ERROR)) { getSIPStack() .getStackLogger() .logError("Error self routing message cause by: ", ex); } } } }; getSIPStack().getSelfRoutingThreadpoolExecutor().execute(processMessageTask); } catch (Exception e) { sipStack.getStackLogger().logError("Error passing message in self routing", e); } if (sipStack.isLoggingEnabled(LogWriter.TRACE_DEBUG)) sipStack.getStackLogger().logDebug("Self routing message"); return; } } } encapsulatedChannel.sendMessage(messageToSend, this.peerInetAddress, this.peerPort); } finally { this.startTransactionTimer(); } }
/** Close the encapsulated channel. */ public void close() { this.encapsulatedChannel.close(); if (sipStack.isLoggingEnabled(LogWriter.TRACE_DEBUG)) sipStack.getStackLogger().logDebug("Closing " + this.encapsulatedChannel); }
/** * A method that can be used to test if an incoming request belongs to this transction. This does * not take the transaction state into account when doing the check otherwise it is identical to * isMessagePartOfTransaction. This is useful for checking if a CANCEL belongs to this * transaction. * * @param requestToTest is the request to test. * @return true if the the request belongs to the transaction. */ public boolean doesCancelMatchTransaction(SIPRequest requestToTest) { // List of Via headers in the message to test ViaList viaHeaders; // Topmost Via header in the list Via topViaHeader; // Branch code in the topmost Via header String messageBranch; // Flags whether the select message is part of this transaction boolean transactionMatches; transactionMatches = false; if (this.getOriginalRequest() == null || this.getOriginalRequest().getMethod().equals(Request.CANCEL)) return false; // Get the topmost Via header and its branch parameter viaHeaders = requestToTest.getViaHeaders(); if (viaHeaders != null) { topViaHeader = (Via) viaHeaders.getFirst(); messageBranch = topViaHeader.getBranch(); if (messageBranch != null) { // If the branch parameter exists but // does not start with the magic cookie, if (!messageBranch.toLowerCase().startsWith(SIPConstants.BRANCH_MAGIC_COOKIE_LOWER_CASE)) { // Flags this as old // (RFC2543-compatible) client // version messageBranch = null; } } // If a new branch parameter exists, if (messageBranch != null && this.getBranch() != null) { // If the branch equals the branch in // this message, if (getBranch().equalsIgnoreCase(messageBranch) && topViaHeader .getSentBy() .equals(((Via) getOriginalRequest().getViaHeaders().getFirst()).getSentBy())) { transactionMatches = true; if (sipStack.isLoggingEnabled(LogWriter.TRACE_DEBUG)) sipStack.getStackLogger().logDebug("returning true"); } } else { // If this is an RFC2543-compliant message, // If RequestURI, To tag, From tag, // CallID, CSeq number, and top Via // headers are the same, if (sipStack.isLoggingEnabled(LogWriter.TRACE_DEBUG)) sipStack.getStackLogger().logDebug("testing against " + getOriginalRequest()); if (getOriginalRequest().getRequestURI().equals(requestToTest.getRequestURI()) && getOriginalRequest().getTo().equals(requestToTest.getTo()) && getOriginalRequest().getFrom().equals(requestToTest.getFrom()) && getOriginalRequest() .getCallId() .getCallId() .equals(requestToTest.getCallId().getCallId()) && getOriginalRequest().getCSeq().getSeqNumber() == requestToTest.getCSeq().getSeqNumber() && topViaHeader.equals(getOriginalRequest().getViaHeaders().getFirst())) { transactionMatches = true; } } } // JvB: Need to pass the CANCEL to the listener! Retransmitted INVITEs // set it to false if (transactionMatches) { this.setPassToListener(); } return transactionMatches; }
public void rescheduleKeepAliveTimeout(long newKeepAliveTimeout) { // long now = System.currentTimeMillis(); // long lastKeepAliveReceivedTimeOrNow = lastKeepAliveReceivedTime == 0 ? now : // lastKeepAliveReceivedTime; // // long newScheduledTime = lastKeepAliveReceivedTimeOrNow + newKeepAliveTimeout; StringBuilder methodLog = new StringBuilder(); if (logger.isLoggingEnabled(LogWriter.TRACE_DEBUG)) { methodLog.append( "~~~ rescheduleKeepAliveTimeout for MessageChannel(key=" + key + "), clientAddress=" + peerAddress + ", clientPort=" + peerPort + ", timeout=" + keepAliveTimeout + "): newKeepAliveTimeout="); if (newKeepAliveTimeout == Long.MAX_VALUE) { methodLog.append("Long.MAX_VALUE"); } else { methodLog.append(newKeepAliveTimeout); } // methodLog.append(", lastKeepAliveReceivedTimeOrNow="); // methodLog.append(lastKeepAliveReceivedTimeOrNow); // methodLog.append(", newScheduledTime="); // methodLog.append(newScheduledTime); } // long delay = newScheduledTime > now ? newScheduledTime - now : 1; try { keepAliveSemaphore.acquire(); } catch (InterruptedException e) { logger.logWarning("Couldn't acquire keepAliveSemaphore"); return; } try { if (pingKeepAliveTimeoutTask == null) { pingKeepAliveTimeoutTask = new KeepAliveTimeoutTimerTask(); if (logger.isLoggingEnabled(LogWriter.TRACE_DEBUG)) { methodLog.append(", scheduling pingKeepAliveTimeoutTask to execute after "); methodLog.append(keepAliveTimeout / 1000); methodLog.append(" seconds"); logger.logDebug(methodLog.toString()); } sipStack.getTimer().schedule(pingKeepAliveTimeoutTask, keepAliveTimeout); } else { if (logger.isLoggingEnabled(LogWriter.TRACE_DEBUG)) { logger.logDebug( "~~~ cancelPingKeepAliveTimeout for MessageChannel(key=" + key + "), clientAddress=" + peerAddress + ", clientPort=" + peerPort + ", timeout=" + keepAliveTimeout + ")"); } sipStack.getTimer().cancel(pingKeepAliveTimeoutTask); pingKeepAliveTimeoutTask = new KeepAliveTimeoutTimerTask(); if (logger.isLoggingEnabled(LogWriter.TRACE_DEBUG)) { methodLog.append(", scheduling pingKeepAliveTimeoutTask to execute after "); methodLog.append(keepAliveTimeout / 1000); methodLog.append(" seconds"); logger.logDebug(methodLog.toString()); } sipStack.getTimer().schedule(pingKeepAliveTimeoutTask, keepAliveTimeout); } } finally { keepAliveSemaphore.release(); } }