/** * Find a matching client SUBSCRIBE to the incoming notify. NOTIFY requests are matched to such * SUBSCRIBE requests if they contain the same "Call-ID", a "To" header "tag" parameter which * matches the "From" header "tag" parameter of the SUBSCRIBE, and the same "Event" header field. * Rules for comparisons of the "Event" headers are described in section 7.2.1. If a matching * NOTIFY request contains a "Subscription-State" of "active" or "pending", it creates a new * subscription and a new dialog (unless they have already been created by a matching response, as * described above). * * @param notifyMessage */ public SIPClientTransaction findSubscribeTransaction(SIPRequest notifyMessage) { synchronized (clientTransactions) { Iterator<SIPClientTransaction> it = clientTransactions.iterator(); String thisToTag = notifyMessage.getTo().getTag(); if (thisToTag == null) return null; Event eventHdr = (Event) notifyMessage.getHeader(EventHeader.NAME); if (eventHdr == null) return null; while (it.hasNext()) { SIPClientTransaction ct = (SIPClientTransaction) it.next(); // SIPRequest sipRequest = ct.getOriginalRequest(); String fromTag = ct.from.getTag(); Event hisEvent = ct.event; // Event header is mandatory but some slopply clients // dont include it. if (hisEvent == null) continue; if (ct.method.equals(Request.SUBSCRIBE) && fromTag.equalsIgnoreCase(thisToTag) && hisEvent != null && eventHdr.match(hisEvent) && notifyMessage.getCallId().getCallId().equalsIgnoreCase(ct.callId.getCallId())) return ct; } } return null; }
/** * 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; }
/** * Handles a new SIP response. It finds a client transaction to handle this message. If none * exists, it sends the message directly to the superclass. * * @param responseReceived Response to handle. * @param responseMessageChannel Channel that received message. * @return A client transaction. */ protected ServerResponseInterface newSIPServerResponse( SIPResponse responseReceived, MessageChannel responseMessageChannel) { // System.out.println("response = " + responseReceived.encode()); // Iterator through all client transactions Iterator<SIPClientTransaction> transactionIterator; // Next transaction in the set SIPClientTransaction nextTransaction; // Transaction to handle this request SIPClientTransaction currentTransaction; String key = responseReceived.getTransactionId(); currentTransaction = (SIPClientTransaction) clientTransactionTable.get(key); if (currentTransaction == null || !currentTransaction.isMessagePartOfTransaction(responseReceived)) { // Loop through all server transactions synchronized (clientTransactions) { transactionIterator = clientTransactions.iterator(); currentTransaction = null; while (transactionIterator.hasNext() && currentTransaction == null) { nextTransaction = (SIPClientTransaction) transactionIterator.next(); // If this transaction should handle this request, if (nextTransaction.isMessagePartOfTransaction(responseReceived)) { // Mark this transaction as the one to // handle this message currentTransaction = nextTransaction; } } } // If no transaction exists to handle this message, if (currentTransaction == null) { // Pass the message directly to the TU return super.newSIPServerResponse(responseReceived, responseMessageChannel); } } // Set ths transaction's encapsulated response interface // from the superclass currentTransaction.setResponseInterface( super.newSIPServerResponse(responseReceived, currentTransaction)); return currentTransaction; }
/** * Removes from the specified list of candidates providers connected to a registrar that does not * match the IP address that we are receiving a request from. * * @param candidates the list of providers we've like to filter. * @param request the request that we are currently dispatching */ private void filterByAddress(List<ProtocolProviderServiceSipImpl> candidates, Request request) { Iterator<ProtocolProviderServiceSipImpl> iterPP = candidates.iterator(); while (iterPP.hasNext()) { ProtocolProviderServiceSipImpl candidate = iterPP.next(); if (candidate.getRegistrarConnection() == null) { // RegistrarLess connections are ok continue; } if (!candidate.getRegistrarConnection().isRegistrarless() && !candidate.getRegistrarConnection().isRequestFromSameConnection(request)) { iterPP.remove(); } } }
/** * Find the <tt>ProtocolProviderServiceSipImpl</tt> (one of our "candidate recipient" listeners) * which this <tt>request</tt> should be dispatched to. The strategy is to look first at the * request URI, and then at the To field to find a matching candidate for dispatching. Note that * this method takes a <tt>Request</tt> as param, and not a <tt>ServerTransaction</tt>, because * sometimes <tt>RequestEvent</tt>s have no associated <tt>ServerTransaction</tt>. * * @param request the <tt>Request</tt> to find a recipient for. * @return a suitable <tt>ProtocolProviderServiceSipImpl</tt>. */ private ProtocolProviderServiceSipImpl findTargetFor(Request request) { if (request == null) { logger.error("request shouldn't be null."); return null; } List<ProtocolProviderServiceSipImpl> currentListenersCopy = new ArrayList<ProtocolProviderServiceSipImpl>(this.getSipListeners()); // Let's first narrow down candidate choice by comparing // addresses and ports (no point in delivering to a provider with a // non matching IP address since they will reject it anyway). filterByAddress(currentListenersCopy, request); if (currentListenersCopy.size() == 0) { logger.error("no listeners"); return null; } URI requestURI = request.getRequestURI(); if (requestURI.isSipURI()) { String requestUser = ((SipURI) requestURI).getUser(); List<ProtocolProviderServiceSipImpl> candidates = new ArrayList<ProtocolProviderServiceSipImpl>(); // check if the Request-URI username is // one of ours usernames for (ProtocolProviderServiceSipImpl listener : currentListenersCopy) { String ourUserID = listener.getAccountID().getUserID(); // logger.trace(ourUserID + " *** " + requestUser); if (ourUserID.equals(requestUser)) { if (logger.isTraceEnabled()) logger.trace("suitable candidate found: " + listener.getAccountID()); candidates.add(listener); } } // the perfect match // every other case is approximation if (candidates.size() == 1) { ProtocolProviderServiceSipImpl perfectMatch = candidates.get(0); if (logger.isTraceEnabled()) logger.trace("Will dispatch to \"" + perfectMatch.getAccountID() + "\""); return perfectMatch; } // more than one account match if (candidates.size() > 1) { // check if a custom param exists in the contact // address (set for registrar accounts) for (ProtocolProviderServiceSipImpl candidate : candidates) { String hostValue = ((SipURI) requestURI).getParameter(SipStackSharing.CONTACT_ADDRESS_CUSTOM_PARAM_NAME); if (hostValue == null) continue; if (hostValue.equals(candidate.getContactAddressCustomParamValue())) { if (logger.isTraceEnabled()) logger.trace( "Will dispatch to \"" + candidate.getAccountID() + "\" because " + "\" the custom param was set"); return candidate; } } // Past this point, our guess is not reliable. We try to find // the "least worst" match based on parameters like the To field // check if the To header field host part // matches any of our SIP hosts for (ProtocolProviderServiceSipImpl candidate : candidates) { URI fromURI = ((FromHeader) request.getHeader(FromHeader.NAME)).getAddress().getURI(); if (fromURI.isSipURI() == false) continue; SipURI ourURI = (SipURI) candidate.getOurSipAddress((SipURI) fromURI).getURI(); String ourHost = ourURI.getHost(); URI toURI = ((ToHeader) request.getHeader(ToHeader.NAME)).getAddress().getURI(); if (toURI.isSipURI() == false) continue; String toHost = ((SipURI) toURI).getHost(); // logger.trace(toHost + "***" + ourHost); if (toHost.equals(ourHost)) { if (logger.isTraceEnabled()) logger.trace( "Will dispatch to \"" + candidate.getAccountID() + "\" because " + "host in the To: is the same as in our AOR"); return candidate; } } // fallback on the first candidate ProtocolProviderServiceSipImpl target = candidates.iterator().next(); logger.info( "Will randomly dispatch to \"" + target.getAccountID() + "\" because there is ambiguity on the username from" + " the Request-URI"); if (logger.isTraceEnabled()) logger.trace("\n" + request); return target; } // fallback on any account ProtocolProviderServiceSipImpl target = currentListenersCopy.iterator().next(); if (logger.isDebugEnabled()) logger.debug( "Will randomly dispatch to \"" + target.getAccountID() + "\" because the username in the Request-URI " + "is unknown or empty"); if (logger.isTraceEnabled()) logger.trace("\n" + request); return target; } else { logger.error("Request-URI is not a SIP URI, dropping"); } return null; }
/** * 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; }