/** * Add a new server transaction to the set of existing transactions. Add it to the top of the list * so an incoming ack has less work to do in order to find the transaction. * * @param serverTransaction -- server transaction to add to the set. */ public void addTransaction(SIPServerTransaction serverTransaction) throws IOException { if (LogWriter.needsLogging) logWriter.logMessage("added transaction " + serverTransaction); synchronized (serverTransactions) { this.serverTransactions.add(0, serverTransaction); serverTransaction.map(); } addTransactionHash(serverTransaction); serverTransaction.startTransactionTimer(); }
/** * Find the transaction corresponding to a given request. * * @param sipMessage request for which to retrieve the transaction. * @param isServer search the server transaction table if true. * @return the transaction object corresponding to the request or null if no such mapping exists. */ public SIPTransaction findTransaction(SIPMessage sipMessage, boolean isServer) { SIPTransaction retval = null; if (isServer) { Via via = sipMessage.getTopmostVia(); if (via.getBranch() != null) { String key = sipMessage.getTransactionId(); synchronized (this.serverTransactionTable) { retval = (SIPTransaction) serverTransactionTable.get(key); if (LogWriter.needsLogging) logMessage("looking for key " + key); if (retval != null && retval.isMessagePartOfTransaction(sipMessage)) return retval; } } // Need to scan the table for old style transactions (RFC 2543 // style) synchronized (this.serverTransactions) { Iterator<SIPServerTransaction> it = serverTransactions.iterator(); while (it.hasNext()) { SIPServerTransaction sipServerTransaction = (SIPServerTransaction) it.next(); if (sipServerTransaction.isMessagePartOfTransaction(sipMessage)) return sipServerTransaction; } } } else { Via via = sipMessage.getTopmostVia(); if (via.getBranch() != null) { String key = sipMessage.getTransactionId(); synchronized (this.clientTransactionTable) { retval = (SIPTransaction) clientTransactionTable.get(key); if (retval != null && retval.isMessagePartOfTransaction(sipMessage)) return retval; } } // Need to scan the table for old style transactions (RFC 2543 // style) synchronized (this.clientTransactions) { Iterator<SIPClientTransaction> it = clientTransactions.iterator(); while (it.hasNext()) { SIPClientTransaction clientTransaction = (SIPClientTransaction) it.next(); if (clientTransaction.isMessagePartOfTransaction(sipMessage)) return clientTransaction; } } } return null; }
public SIPServerTransaction findPendingTransaction(SIPRequest requestReceived) { SIPServerTransaction currentTransaction; Iterator<SIPServerTransaction> transactionIterator; synchronized (pendingTransactions) { transactionIterator = pendingTransactions.iterator(); currentTransaction = null; while (transactionIterator.hasNext() && currentTransaction == null) { SIPServerTransaction nextTransaction = (SIPServerTransaction) transactionIterator.next(); // If this transaction should handle this request, if (nextTransaction.isMessagePartOfTransaction(requestReceived)) { // Mark this transaction as the one // to handle this message currentTransaction = nextTransaction; } } } return currentTransaction; }
/** * Get the transaction to cancel. Search the server transaction table for a transaction that * matches the given transaction. */ public SIPTransaction findCancelTransaction(SIPRequest cancelRequest, boolean isServer) { if (LogWriter.needsLogging) { logWriter.logMessage( "findCancelTransaction request= \n" + cancelRequest + "\nfindCancelRequest isServer=" + isServer); } if (isServer) { synchronized (this.serverTransactions) { Iterator<SIPServerTransaction> li = this.serverTransactions.iterator(); while (li.hasNext()) { SIPTransaction transaction = (SIPTransaction) li.next(); // SIPRequest sipRequest = (SIPRequest) (transaction // .getRequest()); SIPServerTransaction sipServerTransaction = (SIPServerTransaction) transaction; if (sipServerTransaction.doesCancelMatchTransaction(cancelRequest)) return sipServerTransaction; } } } else { synchronized (this.clientTransactions) { Iterator<SIPClientTransaction> li = this.clientTransactions.iterator(); while (li.hasNext()) { SIPTransaction transaction = (SIPTransaction) li.next(); // SIPRequest sipRequest = (SIPRequest) (transaction // .getRequest()); SIPClientTransaction sipClientTransaction = (SIPClientTransaction) transaction; if (sipClientTransaction.doesCancelMatchTransaction(cancelRequest)) return sipClientTransaction; } } } if (LogWriter.needsLogging) logWriter.logMessage("Could not find transaction for cancel request"); return null; }
/** * 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 { } }
/** * 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!"); } } } }
/** * Handles a new SIP request. It finds a server transaction to handle this message. If none * exists, it creates a new transaction. * * @param requestReceived Request to handle. * @param requestMessageChannel Channel that received message. * @return A server transaction. */ protected ServerRequestInterface newSIPServerRequest( SIPRequest requestReceived, MessageChannel requestMessageChannel) { // Iterator through all server transactions Iterator<SIPServerTransaction> transactionIterator; // Next transaction in the set SIPServerTransaction nextTransaction; // Transaction to handle this request SIPServerTransaction currentTransaction; String key = requestReceived.getTransactionId(); currentTransaction = (SIPServerTransaction) serverTransactionTable.get(key); if (currentTransaction == null || !currentTransaction.isMessagePartOfTransaction(requestReceived)) { // Loop through all server transactions synchronized (serverTransactions) { transactionIterator = serverTransactions.iterator(); currentTransaction = null; while (transactionIterator.hasNext() && currentTransaction == null) { nextTransaction = (SIPServerTransaction) transactionIterator.next(); // If this transaction should handle this request, if (nextTransaction.isMessagePartOfTransaction(requestReceived)) { // Mark this transaction as the one // to handle this message currentTransaction = nextTransaction; } } // If no transaction exists to handle this message if (currentTransaction == null) { currentTransaction = findPendingTransaction(requestReceived); if (currentTransaction != null) return currentTransaction; currentTransaction = createServerTransaction(requestMessageChannel); currentTransaction.setOriginalRequest(requestReceived); if (!isDialogCreated(requestReceived.getMethod())) { // Dialog is not created - can we find the state? // If so, then create a transaction and add it. String dialogId = requestReceived.getDialogId(true); SIPDialog dialog = getDialog(dialogId); // Sequence numbers are supposed to increment. // avoid processing old sequence numbers and // delivering the same request up to the // application if the request has already been seen. // Special handling applies to ACK processing. if (dialog != null && (requestReceived.getMethod().equals(Request.ACK) || requestReceived.getCSeq().getSequenceNumber() > dialog.getRemoteSequenceNumber())) { // Found a dialog. if (LogWriter.needsLogging) logWriter.logMessage("adding server transaction " + currentTransaction); serverTransactions.add(0, currentTransaction); addTransactionHash(currentTransaction); currentTransaction.startTransactionTimer(); currentTransaction.isMapped = true; } } else { // Create the transaction but dont map it. String dialogId = requestReceived.getDialogId(true); SIPDialog dialog = getDialog(dialogId); // This is a dialog creating request that is part of an // existing dialog (eg. re-Invite). Re-invites get a non // null server transaction Id (unlike the original // invite). if (dialog != null && requestReceived.getCSeq().getSequenceNumber() > dialog.getRemoteSequenceNumber()) { currentTransaction.map(); if (LogWriter.needsLogging) logWriter.logMessage("adding server transaction " + currentTransaction); serverTransactions.add(0, currentTransaction); addTransactionHash(currentTransaction); currentTransaction.startTransactionTimer(); currentTransaction.toListener = true; } } } } } // Set ths transaction's encapsulated request // interface from the superclass currentTransaction.setRequestInterface( super.newSIPServerRequest(requestReceived, currentTransaction)); return currentTransaction; }