/** The real task work, replace listening point. */ public void run() { // if the provider is still unregistering it most probably won't // successes until we re-init the LP if (protocolProvider.getRegistrationState() == RegistrationState.UNREGISTERING) { String transport = protocolProvider.getRegistrarConnection().getTransport(); ListeningPoint old = getLP(transport); try { stack.deleteListeningPoint(old); } catch (Throwable t) { logger.warn("Error replacing ListeningPoint for " + transport, t); } try { ListeningPoint tcpLP = stack.createListeningPoint( NetworkUtils.IN_ADDR_ANY, transport.equals(ListeningPoint.TCP) ? getPreferredClearPort() : getPreferredSecurePort(), transport); clearJainSipProvider.addListeningPoint(tcpLP); } catch (Throwable t) { logger.warn( "Error replacing ListeningPoint for " + protocolProvider.getRegistrarConnection().getTransport(), t); } } resetListeningPointsTimers.remove(protocolProvider.getRegistrarConnection().getTransport()); }
/** * Dispatches the event received from a JAIN-SIP <tt>SipProvider</tt> to one of our "candidate * recipient" listeners. * * @param event the event received for a <tt>SipProvider</tt>. */ public void processTimeout(TimeoutEvent event) { try { Transaction transaction; if (event.isServerTransaction()) { transaction = event.getServerTransaction(); } else { transaction = event.getClientTransaction(); } ProtocolProviderServiceSipImpl recipient = getServiceData(transaction); if (recipient == null) { logger.error( "We received a timeout which wasn't " + "marked, please report this to " + "*****@*****.**"); } else { recipient.processTimeout(event); } } catch (Throwable exc) { // any exception thrown within our code should be caught here // so that we could log it rather than interrupt stack activity with // it. this.logApplicationException(DialogTerminatedEvent.class, exc); } }
/** * Notified when registration state changed for a provider. * * @param evt */ public void registrationStateChanged(RegistrationStateChangeEvent evt) { if (evt.getNewState() == RegistrationState.UNREGISTERING) { new Timer().schedule(this, TIME_FOR_PP_TO_UNREGISTER); } else { protocolProvider.removeRegistrationStateChangeListener(this); resetListeningPointsTimers.remove(protocolProvider.getRegistrarConnection().getTransport()); } }
/** * 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(); } } }
/** * Dispatches the event received from a JAIN-SIP <tt>SipProvider</tt> to one of our "candidate * recipient" listeners. * * @param event the event received for a <tt>SipProvider</tt>. */ public void processResponse(ResponseEvent event) { try { // we don't have to accept the transaction since we // created the request ClientTransaction transaction = event.getClientTransaction(); if (logger.isTraceEnabled()) logger.trace( "received response: " + event.getResponse().getStatusCode() + " " + event.getResponse().getReasonPhrase()); if (transaction == null) { logger.warn("Transaction is null, probably already expired!"); return; } ProtocolProviderServiceSipImpl service = getServiceData(transaction); if (service != null) { // Mark the dialog for the dispatching of later in-dialog // responses. If there is no dialog then the initial request // sure is marked otherwise we won't have found the service with // getServiceData(). The request has to be marked in case we // receive one more response in an out-of-dialog transaction. if (event.getDialog() != null) { SipApplicationData.setApplicationData( event.getDialog(), SipApplicationData.KEY_SERVICE, service); } service.processResponse(event); } else { logger.error( "We received a response which " + "wasn't marked, please report this to " + "*****@*****.**"); } } catch (Throwable exc) { // any exception thrown within our code should be caught here // so that we could log it rather than interrupt stack activity with // it. this.logApplicationException(DialogTerminatedEvent.class, exc); } }
/** * Dispatches the event received from a JAIN-SIP <tt>SipProvider</tt> to one of our "candidate * recipient" listeners. * * @param event the event received for a <tt>SipProvider</tt>. */ public void processDialogTerminated(DialogTerminatedEvent event) { try { ProtocolProviderServiceSipImpl recipient = (ProtocolProviderServiceSipImpl) SipApplicationData.getApplicationData( event.getDialog(), SipApplicationData.KEY_SERVICE); if (recipient == null) { logger.error( "Dialog wasn't marked, please report this to " + "*****@*****.**"); } else { if (logger.isTraceEnabled()) logger.trace("service was found with dialog data"); recipient.processDialogTerminated(event); } } catch (Throwable exc) { // any exception thrown within our code should be caught here // so that we could log it rather than interrupt stack activity with // it. this.logApplicationException(DialogTerminatedEvent.class, exc); } }
/** * Listens for network changes and if we have a down interface and we have a tcp/tls provider * which is staying for 20 seconds in unregistering state, it cannot unregister cause its using * the old address which is currently down, and we must recreate its listening points so it can * further reconnect. * * @param event the change event. */ public void configurationChanged(ChangeEvent event) { if (event.isInitial()) return; if (event.getType() == ChangeEvent.ADDRESS_DOWN) { for (final ProtocolProviderServiceSipImpl pp : listeners) { if (pp.getRegistrarConnection().getTransport() != null && (pp.getRegistrarConnection().getTransport().equals(ListeningPoint.TCP) || pp.getRegistrarConnection().getTransport().equals(ListeningPoint.TLS))) { ResetListeningPoint reseter; synchronized (resetListeningPointsTimers) { // we do this only once for transport if (resetListeningPointsTimers.containsKey(pp.getRegistrarConnection().getTransport())) continue; reseter = new ResetListeningPoint(pp); resetListeningPointsTimers.put(pp.getRegistrarConnection().getTransport(), reseter); } pp.addRegistrationStateChangeListener(reseter); } } } }
/** * 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; }
/** * Dispatches the event received from a JAIN-SIP <tt>SipProvider</tt> to one of our "candidate * recipient" listeners. * * @param event the event received for a <tt>SipProvider</tt>. */ public void processRequest(RequestEvent event) { try { Request request = event.getRequest(); if (logger.isTraceEnabled()) logger.trace("received request: " + request.getMethod()); /* * Create the transaction if it doesn't exist yet. If it is a * dialog-creating request, the dialog will also be automatically * created by the stack. */ if (event.getServerTransaction() == null) { try { // apply some hacks if needed on incoming request // to be compliant with some servers/clients // if needed stop further processing. if (applyNonConformanceHacks(event)) return; SipProvider source = (SipProvider) event.getSource(); ServerTransaction transaction = source.getNewServerTransaction(request); /* * Update the event, otherwise getServerTransaction() and * getDialog() will still return their previous value. */ event = new RequestEvent(source, transaction, transaction.getDialog(), request); } catch (SipException ex) { logger.error( "couldn't create transaction, please report " + "this to [email protected]", ex); } } ProtocolProviderServiceSipImpl service = getServiceData(event.getServerTransaction()); if (service != null) { service.processRequest(event); } else { service = findTargetFor(request); if (service == null) { logger.error("couldn't find a ProtocolProviderServiceSipImpl " + "to dispatch to"); if (event.getServerTransaction() != null) event.getServerTransaction().terminate(); } else { /* * Mark the dialog for the dispatching of later in-dialog * requests. If there is no dialog, we need to mark the * request to dispatch a possible timeout when sending the * response. */ Object container = event.getDialog(); if (container == null) container = request; SipApplicationData.setApplicationData(container, SipApplicationData.KEY_SERVICE, service); service.processRequest(event); } } } catch (Throwable exc) { /* * Any exception thrown within our code should be caught here so * that we could log it rather than interrupt stack activity with * it. */ this.logApplicationException(DialogTerminatedEvent.class, exc); // Unfortunately, death can hardly be ignored. if (exc instanceof ThreadDeath) throw (ThreadDeath) exc; } }
/** * Attach any custom headers pre configured for the account. Added only to message Requests. The * header account properties are of form: ConfigHeader.N.Name=HeaderName * ConfigHeader.N.Value=HeaderValue ConfigHeader.N.Method=SIP_MethodName (optional) Where N is the * index of the header, multiple custom headers can be added. Name is the header name to use and * Value is its value. The optional property is whether to use a specific request method to attach * headers to or if missing we will attach it to all requests. * * @param message the message that we'd like to attach custom headers to. * @param protocolProvider the protocol provider to check for configured custom headers. */ static void attachConfigHeaders( Message message, ProtocolProviderServiceSipImpl protocolProvider) { if (message instanceof Response) return; Request request = (Request) message; Map<String, String> props = protocolProvider.getAccountID().getAccountProperties(); Map<String, Map<String, String>> headers = new HashMap<String, Map<String, String>>(); // parse the properties into the map where the index is the key for (Map.Entry<String, String> entry : props.entrySet()) { String pName = entry.getKey(); String prefStr = entry.getValue(); String name; String ix; if (!pName.startsWith(ACC_PROPERTY_CONFIG_HEADER) || prefStr == null) continue; prefStr = prefStr.trim(); if (pName.contains(".")) { pName = pName.replaceAll(ACC_PROPERTY_CONFIG_HEADER + ".", ""); name = pName.substring(pName.lastIndexOf('.') + 1).trim(); if (!pName.contains(".")) continue; ix = pName.substring(0, pName.lastIndexOf('.')).trim(); } else continue; Map<String, String> headerValues = headers.get(ix); if (headerValues == null) { headerValues = new HashMap<String, String>(); headers.put(ix, headerValues); } headerValues.put(name, prefStr); } // process the found custom headers for (Map<String, String> headerValues : headers.values()) { String method = headerValues.get(ACC_PROPERTY_CONFIG_HEADER_METHOD); // if there is a method setting and is different from // current request method skip this header // if any of the required properties are missing skip (Name & Value) if ((method != null && !request.getMethod().equalsIgnoreCase(method)) || !headerValues.containsKey(ACC_PROPERTY_CONFIG_HEADER_NAME) || !headerValues.containsKey(ACC_PROPERTY_CONFIG_HEADER_VALUE)) continue; try { String name = headerValues.get(ACC_PROPERTY_CONFIG_HEADER_NAME); String value = processParams(headerValues.get(ACC_PROPERTY_CONFIG_HEADER_VALUE), request); Header h = request.getHeader(name); // makes possible overriding already created headers which // are not custom one // RouteHeader is used by ProxyRouter/DefaultRouter and we // cannot use it as custom header if we want to add it if ((h != null && !(h instanceof CustomHeader)) || name.equals(SIPHeaderNames.ROUTE)) { request.setHeader(protocolProvider.getHeaderFactory().createHeader(name, value)); } else request.addHeader(new CustomHeaderList(name, value)); } catch (Exception e) { logger.error("Cannot create custom header", e); } } }