/* * JBoss, Home of Professional Open Source * Copyright 2011, Red Hat, Inc. and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.mobicents.tools.sip.balancer; import gov.nist.javax.sip.ListeningPointExt; import gov.nist.javax.sip.SipStackImpl; import gov.nist.javax.sip.address.SipUri; import gov.nist.javax.sip.header.HeaderExt; import gov.nist.javax.sip.header.HeaderFactoryImpl; import gov.nist.javax.sip.header.Route; import gov.nist.javax.sip.header.RouteList; import gov.nist.javax.sip.message.ResponseExt; import gov.nist.javax.sip.message.SIPMessage; import gov.nist.javax.sip.message.SIPResponse; import gov.nist.javax.sip.stack.SIPMessageValve; import java.io.ByteArrayInputStream; import java.net.InetAddress; import java.net.UnknownHostException; import java.text.ParseException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.ListIterator; import java.util.Map; import java.util.Properties; import java.util.TooManyListenersException; import java.util.concurrent.atomic.AtomicLong; import java.util.regex.Pattern; import javax.sip.DialogTerminatedEvent; import javax.sip.IOExceptionEvent; import javax.sip.InvalidArgumentException; import javax.sip.ListeningPoint; import javax.sip.ObjectInUseException; import javax.sip.PeerUnavailableException; import javax.sip.RequestEvent; import javax.sip.ResponseEvent; import javax.sip.SipException; import javax.sip.SipFactory; import javax.sip.SipListener; import javax.sip.SipProvider; import javax.sip.TimeoutEvent; import javax.sip.Transaction; import javax.sip.TransactionTerminatedEvent; import javax.sip.TransactionUnavailableException; import javax.sip.TransportAlreadySupportedException; import javax.sip.TransportNotSupportedException; import javax.sip.address.Address; import javax.sip.address.SipURI; import javax.sip.address.TelURL; import javax.sip.address.URI; import javax.sip.header.CallIdHeader; import javax.sip.header.ContactHeader; import javax.sip.header.FromHeader; import javax.sip.header.Header; import javax.sip.header.HeaderAddress; import javax.sip.header.HeaderFactory; import javax.sip.header.MaxForwardsHeader; import javax.sip.header.ReasonHeader; import javax.sip.header.RecordRouteHeader; import javax.sip.header.RouteHeader; import javax.sip.header.ToHeader; import javax.sip.header.ViaHeader; import javax.sip.message.Message; import javax.sip.message.Request; import javax.sip.message.Response; import org.apache.log4j.Logger; import org.mobicents.tools.configuration.LoadBalancerConfiguration; import org.mobicents.tools.heartbeat.api.Node; /** * A transaction stateful UDP Forwarder that listens at a port and forwards to multiple * outbound addresses. It keeps a timer thread around that pings the list of * proxy servers and sends to the first proxy server. * * It uses double record routing to be able to listen on one transport and sends on another transport * or allows support for multihoming. * * @author M. Ranganathan * @author baranowb * @author <A HREF="mailto:jean.deruelle@gmail.com">Jean Deruelle</A> */ public class SIPBalancerForwarder implements SipListener { private static final Logger logger = Logger.getLogger(SIPBalancerForwarder.class .getCanonicalName()); /* * Those parameters is to indicate to the SIP Load Balancer, from which node comes from the request * so that it can stick the Call Id to this node and correctly route the subsequent requests. */ public static final String ROUTE_PARAM_NODE_HOST = "node_host"; public static final String ROUTE_PARAM_NODE_PORT = "node_port"; public static final String ROUTE_PARAM_NODE_VERSION = "version"; public static final int UDP = 0; public static final int TCP = 1; public static final int TLS = 2; public static final int WS = 3; public static final int WSS = 4; BalancerRunner balancerRunner; protected static final HashSet<String> dialogCreationMethods=new HashSet<String>(2); static{ dialogCreationMethods.add(Request.INVITE); dialogCreationMethods.add(Request.SUBSCRIBE); } public NodeRegister register; protected String[] extraServerAddresses; protected int[] extraServerPorts; public SIPBalancerForwarder(LoadBalancerConfiguration lbConfig, BalancerRunner balancerRunner, NodeRegister register) throws IllegalStateException{ super(); this.balancerRunner = balancerRunner; this.balancerRunner.balancerContext.forwarder = this; this.balancerRunner.balancerContext.lbConfig = lbConfig; this.register = register; } public void start() { setSipLoadBalancerProperty(); setLoadBalacerHostsPorts(); String extraServerNodesString = balancerRunner.balancerContext.lbConfig.getSipConfiguration().getExtraServerNodes(); if(extraServerNodesString != null&&!extraServerNodesString.equals("")) setExtraServerNodes(extraServerNodesString); try { createSipStack(); ArrayList <ListeningPoint> listeningPoints = new ArrayList<ListeningPoint>(); ArrayList <ListeningPoint> listeningPointsIpv6 = new ArrayList<ListeningPoint>(); createListeningPoints(balancerRunner.balancerContext.externalPorts, balancerRunner.balancerContext.externalHost, listeningPoints); createListeningPoints(balancerRunner.balancerContext.externalIpv6Ports, balancerRunner.balancerContext.externalIpv6Host, listeningPointsIpv6); if(balancerRunner.balancerContext.externalIpLoadBalancerAddresses != null) { createRecordRouteHeaders(balancerRunner.balancerContext.externalIpLoadBalancerPorts, balancerRunner.balancerContext.externalIpLoadBalancerAddresses.get(0), balancerRunner.balancerContext.activeExternalHeader); createRecordRouteHeaders(balancerRunner.balancerContext.externalIpv6LoadBalancerPorts, balancerRunner.balancerContext.externalIpLoadBalancerAddresses.get(0), balancerRunner.balancerContext.activeExternalIpv6Header); } else { createRecordRouteHeaders(listeningPoints, balancerRunner.balancerContext.activeExternalHeader); createRecordRouteHeaders(listeningPointsIpv6, balancerRunner.balancerContext.activeExternalIpv6Header); } balancerRunner.balancerContext.externalSipProvider = createSipProviders(listeningPoints); if(listeningPointsIpv6.size()!=0) balancerRunner.balancerContext.externalIpv6SipProvider = createSipProviders(listeningPointsIpv6); if(balancerRunner.balancerContext.isTwoEntrypoints()) { listeningPoints = new ArrayList<ListeningPoint>(); listeningPointsIpv6 = new ArrayList<ListeningPoint>(); createListeningPoints(balancerRunner.balancerContext.internalPorts, balancerRunner.balancerContext.internalHost, listeningPoints); createListeningPoints(balancerRunner.balancerContext.internalIpv6Ports, balancerRunner.balancerContext.internalIpv6Host, listeningPointsIpv6); if(balancerRunner.balancerContext.internalIpLoadBalancerAddresses != null) { createRecordRouteHeaders(balancerRunner.balancerContext.internalIpLoadBalancerPorts, balancerRunner.balancerContext.internalIpLoadBalancerAddresses.get(0), balancerRunner.balancerContext.activeInternalHeader); createRecordRouteHeaders(balancerRunner.balancerContext.internalIpv6LoadBalancerPorts, balancerRunner.balancerContext.internalIpLoadBalancerAddresses.get(0), balancerRunner.balancerContext.activeInternalIpv6Header); } else if(balancerRunner.balancerContext.isTwoEntrypoints()) { createRecordRouteHeaders(listeningPoints, balancerRunner.balancerContext.activeInternalHeader); createRecordRouteHeaders(listeningPointsIpv6, balancerRunner.balancerContext.activeInternalIpv6Header); } balancerRunner.balancerContext.internalSipProvider = createSipProviders(listeningPoints); if(listeningPointsIpv6.size()!=0) balancerRunner.balancerContext.internalIpv6SipProvider = createSipProviders(listeningPointsIpv6); } setViaHostsPorts(); balancerRunner.balancerContext.sipStack.start(); for(SIPMessageValve valve : balancerRunner.balancerContext.sipStack.sipMessageValves) if(valve instanceof SIPBalancerValveProcessor) ((SIPBalancerValveProcessor)valve).balancerRunner = balancerRunner; } catch (Exception ex) { throw new IllegalStateException("Can't create sip objects and lps due to["+ex.getMessage()+"]", ex); } if(logger.isInfoEnabled()) printConfigInfo(); } public void stop() { if(balancerRunner.balancerContext.sipStack == null) return;// already stopped @SuppressWarnings("rawtypes") Iterator sipProviderIterator = balancerRunner.balancerContext.sipStack.getSipProviders(); try{ while (sipProviderIterator.hasNext()) { SipProvider sipProvider = (SipProvider)sipProviderIterator.next(); sipProvider.removeSipListener(this); ListeningPoint[] listeningPoints = sipProvider.getListeningPoints(); balancerRunner.balancerContext.sipStack.deleteSipProvider(sipProvider); for (ListeningPoint listeningPoint : listeningPoints) { if(logger.isInfoEnabled()) { logger.info("Removing the following Listening Point " + listeningPoint); } try { sipProvider.removeListeningPoint(listeningPoint); balancerRunner.balancerContext.sipStack.deleteListeningPoint(listeningPoint); } catch (Exception e) { logger.error("Cant remove the listening points or sip providers", e); } } if(logger.isInfoEnabled()) { logger.info("Removing the sip provider"); } sipProviderIterator = balancerRunner.balancerContext.sipStack.getSipProviders(); } balancerRunner.balancerContext.sipStack.stop(); balancerRunner.balancerContext.sipStack = null; System.gc(); if(logger.isInfoEnabled()) { logger.info("Sip forwarder SIP stack stopped"); } } catch (Exception e) { throw new IllegalStateException("Cant remove the listening points or sip providers", e); } } public void processDialogTerminated( DialogTerminatedEvent dialogTerminatedEvent) { // We wont see those } public void processIOException(IOExceptionEvent exceptionEvent) { // Hopefully we wont see those either } /* * (non-Javadoc) * @see javax.sip.SipListener#processRequest(javax.sip.RequestEvent) */ public void processRequest(RequestEvent requestEvent) { // This will be invoked only by external endpoint BalancerAppContent content=(BalancerAppContent)requestEvent.getSource(); boolean isIpv6 = content.isIpv6(); final SipProvider sipProvider = content.getProvider(); final Request request = requestEvent.getRequest(); final String requestMethod = request.getMethod(); if(logger.isDebugEnabled()) { logger.debug("got request:\n"+request); } if((requestMethod.equals(Request.OPTIONS) || requestMethod.equals(Request.INFO)) && request.getHeader("Mobicents-Heartbeat") != null && sipProvider == balancerRunner.balancerContext.internalSipProvider) { byte[] bytes = (byte[]) request.getContent(); Properties prop = new Properties(); try { prop.load(new ByteArrayInputStream(bytes, 0, bytes.length)); Node node = new Node(prop.getProperty("hostname"), prop.getProperty("ip")); for(String id : prop.stringPropertyNames()) { node.getProperties().put(id, prop.getProperty(id)); } ArrayList<Node> list = new ArrayList<Node>(); list.add(node); this.register.handlePingInRegister(list); Response response = balancerRunner.balancerContext.messageFactory.createResponse(Response.OK, request); sipProvider.sendResponse(response); return; } catch (Exception e) { logger.error("Failure parsing heartbeat properties from this request " + request, e); } } //Issue 10: https://telestax.atlassian.net/browse/LB-10 if (request.getContent() != null || (requestMethod.equals(Request.REGISTER) && sipProvider != balancerRunner.balancerContext.internalSipProvider)) { SIPMessage message = (SIPMessage)request; String initialRemoteAddr = message.getPeerPacketSourceAddress().getHostAddress(); String initialRemotePort = String.valueOf(message.getPeerPacketSourcePort()); Header remoteAddrHeader = null; Header remotePortHeader = null; try { remoteAddrHeader = SipFactory.getInstance().createHeaderFactory().createHeader("X-Sip-Balancer-InitialRemoteAddr", initialRemoteAddr); remotePortHeader = SipFactory.getInstance().createHeaderFactory().createHeader("X-Sip-Balancer-InitialRemotePort", initialRemotePort); } catch (PeerUnavailableException e) { logger.error("Unexpected exception while creating custom headers for REGISTER message ", e); } catch (ParseException e) { logger.error("Unexpected exception while creating custom headers for REGISTER message ", e); } if (remoteAddrHeader != null) request.addHeader(remoteAddrHeader); if (remotePortHeader != null) request.addHeader(remotePortHeader); } try { updateStats(request); forwardRequest(sipProvider,request, isIpv6); } catch (Throwable throwable) { logger.error("Unexpected exception while forwarding the request " + request, throwable); if(!Request.ACK.equalsIgnoreCase(requestMethod)) { try { Response response = balancerRunner.balancerContext.messageFactory.createResponse(Response.SERVER_INTERNAL_ERROR, request); sipProvider.sendResponse(response); } catch (Exception e) { logger.error("Unexpected exception while trying to send the error response for this " + request, e); } } } } private void updateStats(Message message) { if(balancerRunner.balancerContext.gatherStatistics) { if(message instanceof Request) { balancerRunner.balancerContext.bytesTransferred.addAndGet(((Request) message).getContentLength().getContentLength()); balancerRunner.balancerContext.requestsProcessed.incrementAndGet(); final String method = ((Request) message).getMethod(); final AtomicLong requestsProcessed = balancerRunner.balancerContext.requestsProcessedByMethod.get(method); if(requestsProcessed == null) { balancerRunner.balancerContext.requestsProcessedByMethod.put(method, new AtomicLong(0)); } else { requestsProcessed.incrementAndGet(); } if(Request.INVITE.equalsIgnoreCase(method)) { balancerRunner.incCalls(); } if(Request.MESSAGE.equalsIgnoreCase(method)) { balancerRunner.incMessages(); } } else { balancerRunner.balancerContext.responsesProcessed.incrementAndGet(); balancerRunner.balancerContext.bytesTransferred.addAndGet(((Response) message).getContentLength().getContentLength()); final int statusCode = ((Response)message).getStatusCode(); int statusCodeDiv = statusCode / 100; switch (statusCodeDiv) { case 1: balancerRunner.balancerContext.responsesProcessedByStatusCode.get("1XX").incrementAndGet(); break; case 2: balancerRunner.balancerContext.responsesProcessedByStatusCode.get("2XX").incrementAndGet(); break; case 3: balancerRunner.balancerContext.responsesProcessedByStatusCode.get("3XX").incrementAndGet(); break; case 4: balancerRunner.balancerContext.responsesProcessedByStatusCode.get("4XX").incrementAndGet(); break; case 5: balancerRunner.balancerContext.responsesProcessedByStatusCode.get("5XX").incrementAndGet(); break; case 6: balancerRunner.balancerContext.responsesProcessedByStatusCode.get("6XX").incrementAndGet(); break; case 7: balancerRunner.balancerContext.responsesProcessedByStatusCode.get("7XX").incrementAndGet(); break; case 8: balancerRunner.balancerContext.responsesProcessedByStatusCode.get("8XX").incrementAndGet(); break; case 9: balancerRunner.balancerContext.responsesProcessedByStatusCode.get("9XX").incrementAndGet(); break; } } } } private Node getAliveNode(String host, int port, String otherTransport, InvocationContext ctx,Boolean isIpV6) { //return getNodeFromCollection(host, port, otherTransport, ctx.nodes); return ctx.sipNodeMap(isIpV6).get(new KeySip(host,port,isIpV6)); } private Node getAliveNodeAnyVersion(String host, int port, String otherTransport) { return getNodeFromCollection(host, port, otherTransport, balancerRunner.balancerContext.aliveNodes); } private Node getNodeFromCollection(String host, int port, String otherTransport, Collection<Node> ctx) { otherTransport = otherTransport.toLowerCase(); for(Node node : ctx) { if(host.equals(node.getHostName()) || host.equals(node.getIp())) { if(Integer.parseInt(node.getProperties().get(otherTransport + "Port")) == port) { return node; } } } return null; } private Node getNodeDeadOrAlive(String host, int port, String otherTransport) { return getNodeFromCollection(host, port, otherTransport, balancerRunner.balancerContext.allNodesEver); } private boolean isViaHeaderFromServer(Request request) { ViaHeader viaHeader = ((ViaHeader)request.getHeader(ViaHeader.NAME)); String host = viaHeader.getHost(); String transport = viaHeader.getTransport(); if(balancerRunner.balancerContext.internalTransport!=null) { if(logger.isDebugEnabled()) { logger.debug("Set internal transport for Via checking (is from server): " + balancerRunner.balancerContext.internalTransport); } transport = balancerRunner.balancerContext.internalTransport; }else if(balancerRunner.balancerContext.terminateTLSTraffic) { if(transport.equalsIgnoreCase(ListeningPoint.TLS)) transport = ListeningPoint.TCP; if(transport.equalsIgnoreCase(ListeningPointExt.WSS)) transport = ListeningPointExt.WS; } if(transport == null) transport = ListeningPoint.UDP; int port = viaHeader.getPort(); if(extraServerAddresses != null) { for(int q=0; q<extraServerAddresses.length; q++) { if(extraServerAddresses[q].equals(host) && extraServerPorts[q] == port) { return true; } } } if(getAliveNodeAnyVersion(host, port, transport) != null) { return true; } return false; } // private Node getTransactionSourceNode(Response response) { // ViaHeader viaHeader = ((ViaHeader)response.getHeader(ViaHeader.NAME)); // String host = viaHeader.getHost(); // String transport = viaHeader.getTransport(); // if(transport == null) transport = ListeningPoint.UDP; // transport = transport.toLowerCase(); // int port = viaHeader.getPort(); // if(extraServerAddresses != null) { // for(int q=0; q<extraServerAddresses.length; q++) { // if(extraServerAddresses[q].equals(host) && extraServerPorts[q] == port) { // return ExtraServerNode.extraServerNode; // } // } // } // Node node = getNodeDeadOrAlive(host, port, transport); // if(node != null) { // return node; // } // return null; // } private Node getSenderNode(Response response) { SIPResponse resp = (SIPResponse) response; String host = resp.getRemoteAddress().getHostAddress(); ViaHeader viaHeader = ((ViaHeader)response.getHeader(ViaHeader.NAME)); String currentTransport = viaHeader.getTransport(); String transport = null; if(balancerRunner.balancerContext.internalTransport!=null) { if(logger.isDebugEnabled()) { logger.debug("Set internal transport for getting sender Node: " + balancerRunner.balancerContext.internalTransport); } transport = balancerRunner.balancerContext.internalTransport; } else if (balancerRunner.balancerContext.terminateTLSTraffic) { if (currentTransport.equalsIgnoreCase(ListeningPoint.TLS)) transport = ListeningPoint.TCP; else if (currentTransport.equalsIgnoreCase(ListeningPointExt.WSS)) transport = ListeningPointExt.WS; else transport = currentTransport; } else { transport = currentTransport; } if(transport == null) transport = ListeningPoint.UDP; int port = resp.getRemotePort(); if(extraServerAddresses != null) { for(int q=0; q<extraServerAddresses.length; q++) { if(extraServerAddresses[q].equals(host) && extraServerPorts[q] == port) { return ExtraServerNode.extraServerNode; } } } Node node = getNodeDeadOrAlive(host, port, transport); if(node != null) { return node; } return null; } public SipURI getLoopbackUri(Request request, boolean isIpv6) { if(logger.isDebugEnabled()) logger.debug("Check request for loop. Request: " + request); SipURI uri = null; RouteHeader route = (RouteHeader) request.getHeader(RouteHeader.NAME); if(route != null) { if(route.getAddress().getURI().isSipURI()) { uri = (SipURI) route.getAddress().getURI(); } } else { if(request.getRequestURI().isSipURI()) { uri = (SipURI) request.getRequestURI(); } } if(uri.getHost().matches(".*[a-zA-Z]+.*")) { if(logger.isDebugEnabled()) logger.debug("We are going to patch URI because it has domain name instead of IP : " + uri); try { uri.setHost(InetAddress.getByName(uri.getHost()).getHostAddress()); } catch (UnknownHostException | ParseException e) { e.printStackTrace(); } } if(uri != null) { if(!isIpv6) { if(uri.getHost().equals(balancerRunner.balancerContext.externalHost) || uri.getHost().equals(balancerRunner.balancerContext.publicIP) || uri.getHost().equals(balancerRunner.balancerContext.externalIpLoadBalancerAddresses)) { int port = uri.getPort(); for(int externalPort : balancerRunner.balancerContext.externalPorts) if(port == externalPort) return uri; } if(uri.getHost().equals(balancerRunner.balancerContext.internalHost) || uri.getHost().equals(balancerRunner.balancerContext.publicIP) || uri.getHost().equals(balancerRunner.balancerContext.internalIpLoadBalancerAddresses)) { int port = uri.getPort(); for(int internalPort : balancerRunner.balancerContext.internalPorts) if(port == internalPort) return uri; } } else { if(uri.getHost().equals(balancerRunner.balancerContext.externalIpv6Host) || uri.getHost().equals(balancerRunner.balancerContext.publicIPv6) || uri.getHost().equals(balancerRunner.balancerContext.externalIpv6LoadBalancerAddresses)) { int port = uri.getPort(); for(int externalIpv6Port : balancerRunner.balancerContext.externalIpv6Ports) if(port == externalIpv6Port) return uri; } if(uri.getHost().equals(balancerRunner.balancerContext.internalIpv6Host) || uri.getHost().equals(balancerRunner.balancerContext.publicIPv6) || uri.getHost().equals(balancerRunner.balancerContext.internalIpv6LoadBalancerAddresses)) { int port = uri.getPort(); for(int internalIpv6Port : balancerRunner.balancerContext.internalIpv6Ports) if(port == internalIpv6Port) return uri; } } } return null; } /** * @param requestEvent * @param sipProvider * @param originalRequest * @param serverTransaction * @param request * @throws ParseException * @throws InvalidArgumentException * @throws SipException * @throws TransactionUnavailableException */ private void forwardRequest(SipProvider sipProvider, Request request, boolean isIpv6) throws ParseException, InvalidArgumentException, SipException, TransactionUnavailableException { if(logger.isDebugEnabled()) { logger.debug("got request:\n"+request); } boolean isRequestFromServer = false; if(!balancerRunner.balancerContext.isTwoEntrypoints()) { isRequestFromServer = isViaHeaderFromServer(request); } else { isRequestFromServer = sipProvider.equals(balancerRunner.balancerContext.internalSipProvider) || sipProvider.equals(balancerRunner.balancerContext.internalIpv6SipProvider); } if(isRequestFromServer) { ViaHeader viaHeader = (ViaHeader)request.getHeader(ViaHeader.NAME); String host = viaHeader.getHost(); if(host.matches(".*[a-zA-Z]+.*")) { try { host = InetAddress.getByName(host).getHostAddress(); } catch (UnknownHostException e) { e.printStackTrace(); } } int port = viaHeader.getPort(); String transport = viaHeader.getTransport().toLowerCase(); Node node = getNodeDeadOrAlive(host, port, transport); if(node!=null) { if(logger.isDebugEnabled()) logger.debug("Updating Timestamp of node: " + node + " because of request from it"); node.updateTimerStamp(); } } final boolean isCancel = Request.CANCEL.equals(request.getMethod()); if(!isCancel) { decreaseMaxForwardsHeader(sipProvider, request); } String outerTransport = ((ViaHeader) request.getHeader(ViaHeader.NAME)) .getTransport().toLowerCase(); if (isRequestFromServer) { Boolean hasTransport = false; if (request.getRequestURI().isSipURI()) { if (((SipUri) request.getRequestURI()).getTransportParam() != null) { outerTransport = ((SipUri) request.getRequestURI()) .getTransportParam(); hasTransport = true; } } if (!hasTransport) { outerTransport = getRouteHeadersMeantForLB(request, isIpv6); if (outerTransport == null) outerTransport = ((ViaHeader) request .getHeader(ViaHeader.NAME)).getTransport() .toLowerCase(); } } RouteHeaderHints hints = removeRouteHeadersMeantForLB(request,isIpv6); String version = hints.version; if(version == null) { version = register.getLatestVersion(); hints.version = version; } InvocationContext ctx = balancerRunner.getInvocationContext(version); final String callID = ((CallIdHeader) request.getHeader(CallIdHeader.NAME)).getCallId(); String transport = null; if(balancerRunner.balancerContext.internalTransport!=null) { if(logger.isDebugEnabled()) { logger.debug("Set internal transport for NODE looking: " + balancerRunner.balancerContext.internalTransport); } transport = balancerRunner.balancerContext.internalTransport.toLowerCase(); }else if (balancerRunner.balancerContext.terminateTLSTraffic) { switch (((ViaHeader) request.getHeader(ViaHeader.NAME)) .getTransport()) { case ListeningPoint.TLS: transport = ListeningPoint.TCP.toLowerCase(); break; case ListeningPointExt.WSS: transport = ListeningPointExt.WS.toLowerCase(); break; case ListeningPointExt.WS: case ListeningPointExt.TCP: case ListeningPointExt.UDP: transport = ((ViaHeader) request.getHeader(ViaHeader.NAME)).getTransport().toLowerCase(); } if(logger.isDebugEnabled()) { logger.debug("Terminate TLS traffic, isRequestFromServer: " + isRequestFromServer + " transport before " + ((ViaHeader) request.getHeader(ViaHeader.NAME)) .getTransport() + ", transport after " + transport); } } else { transport = ((ViaHeader) request.getHeader(ViaHeader.NAME)).getTransport().toLowerCase(); } if(hints.serverAssignedNode !=null) { String headerKey = null; if(balancerRunner.balancerContext.sipHeaderAffinityKey.equalsIgnoreCase(ToHeader.NAME)) { URI currURI=((HeaderAddress)request.getHeader(balancerRunner.balancerContext.sipHeaderAffinityKey)).getAddress().getURI(); if(currURI.isSipURI()) headerKey = ((SipURI)currURI).getUser(); else headerKey = ((TelURL)currURI).getPhoneNumber(); if(balancerRunner.balancerContext.sipHeaderAffinityKeyExclusionPattern != null && balancerRunner.balancerContext.sipHeaderAffinityKeyExclusionPattern.matcher(headerKey).matches()) { headerKey = ((HeaderExt) request.getHeader(balancerRunner.balancerContext.sipHeaderAffinityFallbackKey)).getValue(); } } else if(balancerRunner.balancerContext.sipHeaderAffinityKey.equalsIgnoreCase(FromHeader.NAME)) { headerKey = ((HeaderAddress) request.getHeader(balancerRunner.balancerContext.sipHeaderAffinityKey)).getAddress().getURI().toString(); if(balancerRunner.balancerContext.sipHeaderAffinityKeyExclusionPattern != null && balancerRunner.balancerContext.sipHeaderAffinityKeyExclusionPattern.matcher(headerKey).matches()) { headerKey = ((HeaderExt) request.getHeader(balancerRunner.balancerContext.sipHeaderAffinityFallbackKey)).getValue(); } } else { headerKey = ((HeaderExt) request.getHeader(balancerRunner.balancerContext.sipHeaderAffinityKey)).getValue(); } if(logger.isDebugEnabled()) { logger.debug("headerKey " + headerKey); } if(!request.getMethod().equalsIgnoreCase(Request.ACK)) ctx.balancerAlgorithm.assignToNode(headerKey, hints.serverAssignedNode); if(logger.isDebugEnabled()) { logger.debug("Following node information has been found in one of the route Headers " + hints.serverAssignedNode); } // SipURI loopbackUri = getLoopbackUri(request); // if(loopbackUri != null) { // loopbackUri.setHost(hints.serverAssignedNode.getIp()); // loopbackUri.setPort((Integer) hints.serverAssignedNode.getProperties().get(transport + "Port")); // } } Node nextNode = null; if(isRequestFromServer) { if(logger.isDebugEnabled()) { logger.debug("Request from server"); } Header initialAddrHeader = request.getHeader("X-Sip-Balancer-InitialRemoteAddr"); Header initialPortHeader = request.getHeader("X-Sip-Balancer-InitialRemotePort"); if(initialAddrHeader != null) request.removeHeader(initialAddrHeader.getName()); if(initialPortHeader != null) request.removeHeader(initialPortHeader.getName()); ctx.balancerAlgorithm.processInternalRequest(request); if (request.getMethod().equalsIgnoreCase(Request.INVITE) && ctx.balancerAlgorithm.blockInternalRequest(request)) { Response response = balancerRunner.balancerContext.messageFactory.createResponse(Response.FORBIDDEN, request); response.setReasonPhrase("Destination not allowed"); sipProvider.sendResponse(response); return; } nextNode = hints.serverAssignedNode; if(logger.isDebugEnabled()) { logger.debug("nexNode " + nextNode); } } else { if(logger.isDebugEnabled()) { logger.debug("Request not from server"); } if(hints.serverAssignedNode !=null){ SipURI loopbackUri = getLoopbackUri(request, isIpv6); if(loopbackUri != null) { loopbackUri.setHost(hints.serverAssignedNode.getIp()); loopbackUri.setPort(Integer.parseInt(hints.serverAssignedNode.getProperties().get(transport + "Port"))); } } // Request is NOT from app server, first check if we have hints in Route headers Node assignedNode = hints.serverAssignedNode; // If there are no hints see if there is route header pointing existing node if(assignedNode == null) { RouteHeader nextNodeHeader = (RouteHeader) request.getHeader(RouteHeader.NAME); if(nextNodeHeader != null) { URI uri = nextNodeHeader.getAddress().getURI(); if(uri instanceof SipURI) { SipURI sipUri = (SipURI) uri; assignedNode = getAliveNode(sipUri.getHost(), sipUri.getPort(), transport, ctx,isIpv6); if(logger.isDebugEnabled()) { logger.debug("Found SIP URI " + uri + " |Next node is " + assignedNode); } } } } SipURI assignedUri = null; //boolean nextNodeInRequestUri = false; SipURI originalRouteHeaderUri = null; if(assignedNode == null) { if(hints.subsequentRequest) { RouteHeader header = (RouteHeader) request.getHeader(RouteHeader.NAME); if(header != null) { assignedUri = (SipURI) header.getAddress().getURI(); originalRouteHeaderUri = (SipURI) assignedUri.clone(); request.removeFirst(RouteHeader.NAME); } else { if(request.getRequestURI() instanceof SipURI) { SipURI sipUri =(SipURI) request.getRequestURI(); //nextNodeInRequestUri = true; assignedNode = getAliveNode(sipUri.getHost(), sipUri.getPort(), transport, ctx,isIpv6); } } if(logger.isDebugEnabled()) { logger.debug("Subsequent request -> Found Route Header " + header + " |Next node is " + assignedNode); } } else if(request.getRequestURI() instanceof SipURI) { SipURI sipUri =(SipURI) request.getRequestURI(); //nextNodeInRequestUri = true; assignedNode = getAliveNode(sipUri.getHost(), sipUri.getPort(), transport, ctx,isIpv6); if(logger.isDebugEnabled()) { logger.debug("NOT Subsequent request -> using sipUri " + sipUri + " |Next node is " + assignedNode); } } } if(assignedNode == null) { if(logger.isDebugEnabled()) { logger.debug("assignedNode is null"); } if (!securityCheck(request)){ logger.warn("Request failed at the security check:\n"+request); } else { nextNode = ctx.balancerAlgorithm.processExternalRequest(request,isIpv6); } if(nextNode instanceof NullServerNode) { if(logger.isDebugEnabled()) { logger.debug("Algorithm returned a NullServerNode. We will not attempt to forward this request " + request); } } if(nextNode != null) { if(logger.isDebugEnabled()) { String nodesString = ""; //Object[] nodes = ctx.nodes.toArray(); Object[] nodes = ctx.sipNodeMap(isIpv6).values().toArray(); for(Object n : nodes) { nodesString +=n + " , "; } logger.debug("Next node is not null. Assigned uri is " + assignedUri + "Available nodes: " + nodesString); } //Adding Route Header pointing to the node the sip balancer wants to forward to SipURI routeSipUri; try { if(assignedUri == null) { // If a next node is NOT already assigned in the dialog from previous requests routeSipUri = balancerRunner.balancerContext.addressFactory .createSipURI(null, nextNode.getIp()); } else { // OTHERWISE, a node is already assigned and it's alive routeSipUri = assignedUri; } routeSipUri.setHost(nextNode.getIp()); Integer port = Integer.parseInt(nextNode.getProperties().get(transport + "Port")); if(port == null) { throw new RuntimeException("Port is null in the node properties for transport=" + transport); } routeSipUri.setPort(port); routeSipUri.setTransportParam(transport); routeSipUri.setLrParam(); SipURI uri = (SipURI) request.getRequestURI(); RouteHeader header = (RouteHeader) request.getHeader(RouteHeader.NAME); if (isHeaderExternal(uri.getHost(), uri.getPort(), ((ViaHeader)request.getHeader("Via")).getTransport(), isIpv6) || header != null) { final RouteHeader route = balancerRunner.balancerContext.headerFactory.createRouteHeader( balancerRunner.balancerContext.addressFactory.createAddress(routeSipUri)); request.addFirst(route); // If the request is meant for the AS it must recognize itself in the ruri, so update it too // For http://code.google.com/p/mobicents/issues/detail?id=2132 if(originalRouteHeaderUri != null && request.getRequestURI().isSipURI()) { // we will just compare by hostport id String rurihostid = uri.getHost() + uri.getPort(); String originalhostid = originalRouteHeaderUri.getHost() + originalRouteHeaderUri.getPort(); if(rurihostid.equals(originalhostid)) { uri.setPort(routeSipUri.getPort()); uri.setHost(routeSipUri.getHost()); } } } else { //should not add any routes , packet is destinated to lb uri.setPort(routeSipUri.getPort()); uri.setHost(routeSipUri.getHost()); } } catch (Exception e) { throw new RuntimeException("Error adding route header", e); } } } else { nextNode = ctx.balancerAlgorithm.processAssignedExternalRequest(request, assignedNode); if(logger.isDebugEnabled()) { logger.debug("Next node " + nextNode + " from assignedNode " + assignedNode); } //add Route header for using it for transferring instead of using uri if(nextNode!=null && hints.subsequentRequest && !isRequestFromServer) { if(request.getRequestURI().isSipURI()) { SipURI sipUri =(SipURI) request.getRequestURI(); SipURI routeSipUri = balancerRunner.balancerContext.addressFactory.createSipURI(null, nextNode.getIp()); Integer port = Integer.parseInt(nextNode.getProperties().get(transport + "Port")); //port should not be null since it subsequent request if(port != null) { routeSipUri.setPort(port); routeSipUri.setTransportParam(transport); routeSipUri.setLrParam(); if(!sipUri.getHost().equals(routeSipUri.getHost()) || sipUri.getPort()!=routeSipUri.getPort()) { Boolean oldHeaderMatch=false; Header oldHeader=request.getHeader(RouteHeader.NAME); if(oldHeader!=null) { RouteHeader oldRouteHeader=(RouteHeader)oldHeader; if(oldRouteHeader.getAddress().getURI().isSipURI()) { SipURI oldURI=(SipURI)oldRouteHeader.getAddress().getURI(); if(oldURI.getHost().equals(routeSipUri.getHost()) && oldURI.getPort()==routeSipUri.getPort()) oldHeaderMatch=true; } } if(!oldHeaderMatch) { final RouteHeader route = balancerRunner.balancerContext.headerFactory.createRouteHeader(balancerRunner.balancerContext.addressFactory.createAddress(routeSipUri)); request.addFirst(route); } } } } } } if(nextNode == null) { if(logger.isDebugEnabled()) { logger.debug("No nodes available"); } if(!Request.ACK.equalsIgnoreCase(request.getMethod())) { try { Response response = balancerRunner.balancerContext.messageFactory.createResponse(Response.SERVER_INTERNAL_ERROR, request); response.setReasonPhrase("No nodes available"); sipProvider.sendResponse(response); } catch (Exception e) { logger.error("Unexpected exception while trying to send the error response for this " + request, e); } } return; } else { } } if(logger.isDebugEnabled()) { logger.debug("Next node " + nextNode); } String requestMethod=request.getMethod(); //100 Trying should be sent only if has indent really forwarding request, othewise should send 500 error // https://telestax.atlassian.net/browse/LB-25 improve performance by sending back 100 Trying right away to tame retransmissions. if(balancerRunner.balancerContext.isSendTrying) { logger.debug("Load balancer sends 100 TRYING"); if(requestMethod.equals(Request.INVITE) || requestMethod.equals(Request.SUBSCRIBE) || requestMethod.equals(Request.NOTIFY) || requestMethod.equals(Request.MESSAGE) || requestMethod.equals(Request.REFER) || requestMethod.equals(Request.PUBLISH) || requestMethod.equals(Request.UPDATE)) { try { Response response = balancerRunner.balancerContext.messageFactory.createResponse(Response.TRYING, request); RouteList routeList = ((SIPMessage)request).getRouteHeaders(); if (routeList != null) { Route route = (Route)routeList.getFirst(); SipUri sipUri = (SipUri)route.getAddress().getURI(); if (sipUri.toString().contains("node_host") || sipUri.toString().contains("node_port")) { String nodeHost = sipUri.getParameter("node_host"); int nodePort = Integer.parseInt(sipUri.getParameter("node_port")); ViaHeader viaHeader = (ViaHeader) response.getHeader(ViaHeader.NAME); viaHeader.setHost(nodeHost); viaHeader.setPort(nodePort); } } sipProvider.sendResponse(response); } catch (SipException e) { logger.error("Unexpected exception while sending TRYING", e); } catch (ParseException e) { logger.error("Unexpected exception while sending TRYING", e); } catch (NumberFormatException e) { logger.error("Unexpected exception while sending TRYING", e); } catch (InvalidArgumentException e) { logger.error("Unexpected exception while sending TRYING", e); } } } else { logger.debug("Load balancer do not send 100 TRYING, this option is disabled"); } hints.serverAssignedNode = nextNode; if(!hints.subsequentRequest && dialogCreationMethods.contains(request.getMethod())) { addLBRecordRoute(sipProvider, request, hints, version, isIpv6); } // Stateless proxies must not use internal state or ransom values when creating branch because they // must repeat exactly the same branches for retransmissions final ViaHeader via = (ViaHeader) request.getHeader(ViaHeader.NAME); String newBranch = via.getBranch() + callID.substring(0, Math.min(callID.length(), 5)); // Add the via header to the top of the header list. ViaHeader viaHeaderExternal = null; ViaHeader viaHeaderInternal = null; String externalViaHost = null; String internalViaHost = null; if(!isIpv6) { externalViaHost = balancerRunner.balancerContext.externalViaHost; internalViaHost = balancerRunner.balancerContext.internalViaHost; } else { externalViaHost = balancerRunner.balancerContext.externalIpv6ViaHost; internalViaHost = balancerRunner.balancerContext.internalIpv6ViaHost; } if (!isRequestFromServer) { viaHeaderExternal = balancerRunner.balancerContext.headerFactory.createViaHeader( externalViaHost,balancerRunner.balancerContext.getExternalViaPortByTransport(outerTransport,isIpv6),outerTransport, newBranch + "_" + version); String innerTransport = transport; if(balancerRunner.balancerContext.internalTransport!=null) { if(logger.isDebugEnabled()) { logger.debug("Set internal transport for for creating Via header : " + balancerRunner.balancerContext.internalTransport); } innerTransport = balancerRunner.balancerContext.internalTransport; } else if (balancerRunner.balancerContext.terminateTLSTraffic) { if(logger.isDebugEnabled()) { logger.debug("Terminate TLS traffic, isRequestFromServer: " + isRequestFromServer + " transport before " + innerTransport); } if (innerTransport.equalsIgnoreCase(ListeningPoint.TLS)) innerTransport = ListeningPoint.TCP; else if (innerTransport.equalsIgnoreCase(ListeningPointExt.WSS)) innerTransport = ListeningPointExt.WS; if(logger.isDebugEnabled()) { logger.debug("Terminate TLS traffic, transport after " + innerTransport); } } if (balancerRunner.balancerContext.isTwoEntrypoints()) viaHeaderInternal = balancerRunner.balancerContext.headerFactory.createViaHeader( internalViaHost,balancerRunner.balancerContext.getInternalViaPortByTransport(innerTransport,isIpv6),innerTransport, newBranch + "zsd" + "_" + version); else viaHeaderInternal = balancerRunner.balancerContext.headerFactory.createViaHeader( externalViaHost,balancerRunner.balancerContext.getExternalViaPortByTransport(innerTransport,isIpv6),innerTransport, newBranch + "zsd" + "_" + version); } else { if (balancerRunner.balancerContext.isTwoEntrypoints()) viaHeaderInternal = balancerRunner.balancerContext.headerFactory.createViaHeader( internalViaHost,balancerRunner.balancerContext.getInternalViaPortByTransport(transport,isIpv6),transport, newBranch + "zsd" + "_" + version); else viaHeaderInternal = balancerRunner.balancerContext.headerFactory.createViaHeader( externalViaHost,balancerRunner.balancerContext.getExternalViaPortByTransport(transport,isIpv6),transport, newBranch + "zsd" + "_" + version); // https://github.com/RestComm/load-balancer/issues/67 if (balancerRunner.balancerContext.terminateTLSTraffic) { if(logger.isDebugEnabled()) { logger.debug("Terminate TLS traffic, isRequestFromServer: " + isRequestFromServer + " transport before " + outerTransport); } if (outerTransport.equalsIgnoreCase(ListeningPoint.TCP)) outerTransport = ListeningPoint.TLS; else if (outerTransport.equalsIgnoreCase(ListeningPointExt.WS)) outerTransport = ListeningPointExt.WSS; if(logger.isDebugEnabled()) { logger.debug("Terminate TLS traffic, transport after " + outerTransport); } } viaHeaderExternal = balancerRunner.balancerContext.headerFactory.createViaHeader( externalViaHost,balancerRunner.balancerContext.getExternalViaPortByTransport(outerTransport,isIpv6),outerTransport, newBranch + "_" + version); } if(logger.isDebugEnabled()) { logger.debug("ViaHeaders will be added " + viaHeaderExternal + " and " + viaHeaderInternal); logger.debug("Sending the request:\n" + request + "\n on the other side"); } if(getLoopbackUri(request, isIpv6) != null) { logger.warn("Drop. Cannot forward to loopback the following request: " + request); return; } try { if (!isRequestFromServer) { request.addHeader(viaHeaderExternal); if (viaHeaderInternal != null) request.addHeader(viaHeaderInternal); if(balancerRunner.balancerContext.terminateTLSTraffic) { // https://github.com/RestComm/load-balancer/issues/67 // Patching the contact header for incoming requests so that requests coming out of nodes will use the non secure version ContactHeader contactHeader = (ContactHeader) request.getHeader(ContactHeader.NAME); if (contactHeader != null) { final URI contactURI = contactHeader.getAddress().getURI(); if(logger.isDebugEnabled()) { logger.debug("Patching the contact header " + contactURI + " so that requests coming out of nodes will use the non secure protocol"); } if(contactURI instanceof SipUri) { ((SipUri) contactURI).setTransportParam(transport); logger.debug("new transport " + contactURI + " so that requests coming out of nodes will use the non secure protocol"); } } } else if(balancerRunner.balancerContext.internalTransport!=null) { // https://github.com/RestComm/load-balancer/issues/67 // Patching the contact header for incoming requests so that requests coming out of nodes will use the non secure version ContactHeader contactHeader = (ContactHeader) request.getHeader(ContactHeader.NAME); if (contactHeader != null) { final URI contactURI = contactHeader.getAddress().getURI(); if(logger.isDebugEnabled()) { logger.debug("Patching the contact header " + contactURI + " so that requests coming out of nodes will use correct protocol"); } if(contactURI instanceof SipUri) { ((SipUri) contactURI).setTransportParam(outerTransport); logger.debug("new transport " + contactURI + " so that requests coming out of nodes will use correct protocol"); } } } if(logger.isDebugEnabled()) { logger.debug("Sending the request:\n" + request); } if (balancerRunner.balancerContext.isTwoEntrypoints()) { if(!isIpv6) balancerRunner.balancerContext.internalSipProvider.sendRequest(request); else { balancerRunner.balancerContext.internalIpv6SipProvider.sendRequest(request); } } else { if(!isIpv6) balancerRunner.balancerContext.externalSipProvider.sendRequest(request); else balancerRunner.balancerContext.externalIpv6SipProvider.sendRequest(request); } } else { // Check if the next hop is actually the load balancer again if(viaHeaderInternal != null) request.addHeader(viaHeaderInternal); if(viaHeaderExternal != null) request.addHeader(viaHeaderExternal); if(balancerRunner.balancerContext.terminateTLSTraffic) { // https://github.com/RestComm/load-balancer/issues/67 if(logger.isDebugEnabled()) { logger.debug("terminateTLSTraffic, Patching the request URI and Route Header if present"); } if(request.getRequestURI() instanceof SipUri) { if(logger.isDebugEnabled()) { logger.debug("terminateTLSTraffic, Patching the request URI to use transport " + outerTransport); } ((SipUri)request.getRequestURI()).setTransportParam(outerTransport); } RouteHeader routeHeader = (RouteHeader) request.getHeader(RouteHeader.NAME); if(routeHeader != null && routeHeader.getAddress().getURI() instanceof SipUri) { if(logger.isDebugEnabled()) { logger.debug("terminateTLSTraffic, Patching the Route Header to use transport " + outerTransport); } ((SipUri)routeHeader.getAddress().getURI()).setTransportParam(outerTransport); } ContactHeader contactHeader = (ContactHeader) request.getHeader(ContactHeader.NAME); if(contactHeader != null && contactHeader.getAddress().getURI() instanceof SipUri) { if(logger.isDebugEnabled()) { logger.debug("terminateTLSTraffic, Patching the Contact Header to use transport " + outerTransport); } ((SipUri)contactHeader.getAddress().getURI()).setTransportParam(outerTransport); } } if(logger.isDebugEnabled()) { logger.debug("Sending the request:\n" + request); } if(!isIpv6) balancerRunner.balancerContext.externalSipProvider.sendRequest(request); else balancerRunner.balancerContext.externalIpv6SipProvider.sendRequest(request); } } catch (SipException e) { logger.error("Unexpected exception while forwarding the request \n" + request, e); if(balancerRunner.balancerContext.isSend5xxResponse) try { Response response = balancerRunner.balancerContext.messageFactory.createResponse(Response.SERVICE_UNAVAILABLE, request); response.removeFirst(ViaHeader.NAME); response.removeFirst(ViaHeader.NAME); if(balancerRunner.balancerContext.isSend5xxResponseReasonHeader!=null) { HeaderFactory hf=SipFactory.getInstance().createHeaderFactory(); ReasonHeader reasonHeader = hf.createReasonHeader(transport, balancerRunner.balancerContext.isSend5xxResponseSatusCode, balancerRunner.balancerContext.isSend5xxResponseReasonHeader); response.setHeader(reasonHeader); } sipProvider.sendResponse(response); } catch (SipException ex) { logger.error("Unexpected exception while sending SERVICE_UNAVAILABLE", ex); } catch (ParseException ex) { logger.error("Unexpected exception while sending SERVICE_UNAVAILABLE", ex); } catch (NumberFormatException ex) { logger.error("Unexpected exception while sending SERVICE_UNAVAILABLE", ex); } catch (InvalidArgumentException ex) { logger.error("Unexpected exception while sending SERVICE_UNAVAILABLE", ex); } } } /** * @param request * @return */ private boolean securityCheck(Request request) { // User-Agent: sipcli/v1.8 // User-Agent: friendly-scanner // To: "sipvicious" <sip:100@1.1.1.1> // From: "sipvicious" <sip:100@1.1.1.1>;tag=3336353363346565313363340133313330323436343236 // From: "1" <sip:1@87.202.36.237>;tag=3e7a78de Header userAgentHeader = request.getHeader("User-Agent"); Header toHeader = request.getHeader("To"); Header fromHeader = request.getHeader("From"); for (String blockedValue: balancerRunner.balancerContext.blockedList){ if(userAgentHeader != null && userAgentHeader.toString().toLowerCase().contains(blockedValue.toLowerCase())) { return false; } else if (toHeader != null && toHeader.toString().toLowerCase().contains(blockedValue.toLowerCase())) { return false; } else if (fromHeader != null && fromHeader.toString().toLowerCase().contains(blockedValue.toLowerCase())) { return false; } } return true; } private RecordRouteHeader stampRecordRoute(RecordRouteHeader rrh, RouteHeaderHints hints, String transport) { SipURI uri = (SipURI) rrh.getAddress().getURI(); try { if(hints.serverAssignedNode != null) { if(logger.isDebugEnabled()) { logger.debug("About to stamp RecordRoute for hints:\n"+hints.serverAssignedNode.toString()+"\n"); } uri.setParameter(ROUTE_PARAM_NODE_HOST, hints.serverAssignedNode.getIp()); uri.setParameter(ROUTE_PARAM_NODE_PORT, hints.serverAssignedNode.getProperties().get(transport.toLowerCase()+"Port").toString()); uri.setParameter(ROUTE_PARAM_NODE_VERSION, hints.version); } else { if(logger.isDebugEnabled()) { logger.debug("No serverAssignedNode could be found, not stamping the record route\n"); } } } catch (ParseException e) { logger.warn("Problem adding rrh" ,e); } return rrh; } private void addTwoRecordRoutes(Request request, RecordRouteHeader first,RecordRouteHeader second, RouteHeaderHints hints, String transport) { if(logger.isDebugEnabled()) { logger.debug("adding Record Router Header :" + first); } try { request.addFirst(stampRecordRoute((RecordRouteHeader) first.clone(), hints, transport)); } catch(Exception ex) { //should not occure } if(logger.isDebugEnabled()) { logger.debug("adding Record Router Header :" + second); } try { request.addFirst(stampRecordRoute((RecordRouteHeader) second.clone(), hints, transport)); } catch(Exception ex) { //should not occure } } /** * @param sipProvider * @param request * @param hints * @throws ParseException */ private void addLBRecordRoute(SipProvider sipProvider, Request request, RouteHeaderHints hints, String version, boolean isIpv6) throws ParseException { if(logger.isDebugEnabled()) { if(!isIpv6) logger.debug("adding Record Router Header :" + balancerRunner.balancerContext.activeExternalHeader); else logger.debug("adding IPv6 Record Router Header :" + Arrays.toString(balancerRunner.balancerContext.activeExternalIpv6Header)); } String transport = ((ViaHeader) request.getHeader(ViaHeader.NAME)).getTransport().toLowerCase(); if (sipProvider.equals(balancerRunner.balancerContext.externalSipProvider) || sipProvider.equals(balancerRunner.balancerContext.externalIpv6SipProvider)) { int transportIndex = 0; int internalTransportIndex = 0; if(balancerRunner.balancerContext.internalTransport!=null) { if(logger.isDebugEnabled()) { logger.debug("Set internal transport for adding Record Route): " + balancerRunner.balancerContext.internalTransport); } String currExternalTransport = transport.toUpperCase(); switch (currExternalTransport) { case ListeningPoint.UDP: transportIndex = UDP; break; case ListeningPoint.TCP: transportIndex = TCP; break; case ListeningPoint.TLS: transportIndex = TLS; break; case ListeningPointExt.WS: transportIndex = WS; break; case ListeningPointExt.WSS: transportIndex = WSS; break; } String currInternalTransport = balancerRunner.balancerContext.internalTransport.toUpperCase(); switch (currInternalTransport) { case ListeningPoint.UDP: internalTransportIndex = UDP; break; case ListeningPoint.TCP: internalTransportIndex = TCP; break; case ListeningPoint.TLS: internalTransportIndex = TLS; break; case ListeningPointExt.WS: internalTransportIndex = WS; break; case ListeningPointExt.WSS: internalTransportIndex = WSS; break; } }else { transportIndex = TLS; internalTransportIndex = TLS; if (balancerRunner.balancerContext.terminateTLSTraffic) { internalTransportIndex = TCP; } if (transport.equalsIgnoreCase(ListeningPoint.UDP)) { transportIndex = UDP; internalTransportIndex = UDP; } else if (transport.equalsIgnoreCase(ListeningPoint.TCP)) { transportIndex = TCP; internalTransportIndex = TCP; } else if (transport.equalsIgnoreCase(ListeningPointExt.WS)) { transportIndex = WS; internalTransportIndex = WS; } else if (transport.equalsIgnoreCase(ListeningPointExt.WSS)) { transportIndex = WSS; if (balancerRunner.balancerContext.terminateTLSTraffic) { internalTransportIndex = WS; } else { internalTransportIndex = WSS; } } } // comes from client if(!isIpv6) addTwoRecordRoutes(request,balancerRunner.balancerContext.activeExternalHeader[transportIndex], balancerRunner.balancerContext.activeInternalHeader[internalTransportIndex], hints, transport); else addTwoRecordRoutes(request,balancerRunner.balancerContext.activeExternalIpv6Header[transportIndex], balancerRunner.balancerContext.activeInternalIpv6Header[internalTransportIndex], hints, transport); } else { int transportIndex = TLS; int externalTransportIndex = TLS; String externalTransport = transport; URI requestURI = ((Request) request).getRequestURI(); if (requestURI.isSipURI()) { if (((SipUri) requestURI).getTransportParam() != null) externalTransport = ((SipUri) requestURI).getTransportParam(); } if (transport.equalsIgnoreCase(ListeningPoint.UDP)) transportIndex = UDP; else if (transport.equalsIgnoreCase(ListeningPoint.TCP)) transportIndex = TCP; else if (transport.equalsIgnoreCase(ListeningPointExt.WS)) transportIndex = WS; else if (transport.equalsIgnoreCase(ListeningPointExt.WSS)) transportIndex = WSS; if (externalTransport.equalsIgnoreCase(ListeningPoint.UDP)) externalTransportIndex = UDP; else if (externalTransport.equalsIgnoreCase(ListeningPoint.TCP)) externalTransportIndex = TCP; else if (externalTransport.equalsIgnoreCase(ListeningPointExt.WS)) externalTransportIndex = WS; else if (externalTransport.equalsIgnoreCase(ListeningPointExt.WSS)) externalTransportIndex = WSS; // comes from app server if(!isIpv6) addTwoRecordRoutes( request, balancerRunner.balancerContext.activeInternalHeader[transportIndex], balancerRunner.balancerContext.activeExternalHeader[externalTransportIndex], hints, transport); else addTwoRecordRoutes( request, balancerRunner.balancerContext.activeInternalIpv6Header[transportIndex], balancerRunner.balancerContext.activeExternalIpv6Header[externalTransportIndex], hints, transport); if(logger.isInfoEnabled()) { logger.info("Will patch Request : \"" + request.getRequestURI() + "\" to provide public IP address for the RecordRoute header"); } patchSipMessageForNAT(request,isIpv6); } } /** * This will check if in the route header there is information on which node from the cluster send the request. * If the request is not received from the cluster, this information will not be present. * @param routeHeader the route header to check * @return the corresponding Sip Node */ private Node checkRouteHeaderForSipNode(SipURI routeSipUri) { Node node = null; String hostNode = routeSipUri.getParameter(ROUTE_PARAM_NODE_HOST); String hostPort = routeSipUri.getParameter(ROUTE_PARAM_NODE_PORT); String hostVersion = routeSipUri.getParameter(ROUTE_PARAM_NODE_VERSION); if(hostNode != null && hostPort != null) { int port = Integer.parseInt(hostPort); String transport = routeSipUri.getTransportParam(); if(transport == null) transport = ListeningPoint.UDP; node = register.getNode(hostNode, port, transport, hostVersion); } return node; } private String getRouteHeadersMeantForLB(Request request ,boolean isIpv6) { @SuppressWarnings("unchecked") ListIterator<RouteHeader> headers = request .getHeaders(RouteHeader.NAME); RouteHeader routeHeader = null; if (headers.hasNext()) routeHeader = headers.next(); if (routeHeader != null) { SipURI routeUri = (SipURI) routeHeader.getAddress().getURI(); String transport = ((ViaHeader) request.getHeader("Via")) .getTransport(); if (routeUri.getTransportParam() != null) transport = routeUri.getTransportParam(); Boolean isRouteHeaderExternal = isHeaderExternal(routeUri.getHost(), routeUri.getPort(),transport,isIpv6); if (!isRouteHeaderExternal) { routeHeader = null; if (headers.hasNext()) routeHeader = headers.next(); if (routeHeader != null) { routeUri = (SipURI) routeHeader.getAddress().getURI(); transport = ((ViaHeader) request.getHeader("Via")) .getTransport(); if (routeUri.getTransportParam() != null) transport = routeUri.getTransportParam(); isRouteHeaderExternal=isHeaderExternal(routeUri.getHost(), routeUri.getPort(),transport,isIpv6); if (!isRouteHeaderExternal) return transport; } return transport; } } return null; } /** * Remove the different route headers that are meant for the Load balancer. * There is two cases here : * <ul> * <li>* Requests coming from external and going to the cluster : dialog creating requests can have route header so that they go through the LB and subsequent requests * will have route headers since the LB record routed</li> * <li>* Requests coming from the cluster and going to external : dialog creating requests can have route header so that they go through the LB - those requests will define in the route header * the originating node of the request so that that subsequent requests are routed to the originating node if still alive</li> * </ul> * * @param request */ private RouteHeaderHints removeRouteHeadersMeantForLB(Request request, boolean isIpv6) { if(logger.isDebugEnabled()) { logger.debug("Checking if there is any route headers meant for the LB to remove..."); } Node node = null; String callVersion = null; int numberOfRemovedRouteHeaders = 0; if(balancerRunner.balancerContext.matchingHostnameForRoute!=null) { RouteHeader routeHeader = (RouteHeader) request.getHeader(RouteHeader.NAME); if(routeHeader!=null) { if(logger.isDebugEnabled()) { logger.debug("Matching host name for route is : " + balancerRunner.balancerContext.matchingHostnameForRoute); logger.debug("Matching host name and subdomain: " + balancerRunner.balancerContext.isFilterSubdomain); } if(!balancerRunner.balancerContext.isFilterSubdomain) { if(((SipURI)routeHeader.getAddress().getURI()).getHost().equals(balancerRunner.balancerContext.matchingHostnameForRoute)) request.removeFirst(RouteHeader.NAME); } else { if(((SipURI)routeHeader.getAddress().getURI()).getHost().endsWith("."+balancerRunner.balancerContext.matchingHostnameForRoute)) request.removeFirst(RouteHeader.NAME); } } } //Removing first routeHeader if it is for the sip balancer RouteHeader routeHeader = (RouteHeader) request.getHeader(RouteHeader.NAME); if(routeHeader != null) { SipURI routeUri = (SipURI)routeHeader.getAddress().getURI(); callVersion = routeUri.getParameter(ROUTE_PARAM_NODE_VERSION); String transport = ((ViaHeader) request.getHeader("Via")).getTransport(); if (routeUri.getTransportParam() != null) transport = routeUri.getTransportParam(); if (!isHeaderExternal(routeUri.getHost(), routeUri.getPort(), transport, isIpv6)) { if (logger.isDebugEnabled()) { logger.debug("this route header is for the LB removing it " + routeUri); } numberOfRemovedRouteHeaders ++; request.removeFirst(RouteHeader.NAME); routeHeader = (RouteHeader) request.getHeader(RouteHeader.NAME); //since we used double record routing we may have 2 routes corresponding to us here // for ACK and BYE from caller for example node = checkRouteHeaderForSipNode(routeUri); if(routeHeader != null) { routeUri = (SipURI)routeHeader.getAddress().getURI(); transport = ((ViaHeader) request.getHeader("Via")).getTransport(); if (routeUri.getTransportParam() != null) transport = routeUri.getTransportParam(); if (!isHeaderExternal(routeUri.getHost(),routeUri.getPort(), transport, isIpv6)) { if (logger.isDebugEnabled()) { logger.debug("this route header is for the LB removing it " + routeUri); } numberOfRemovedRouteHeaders ++; request.removeFirst(RouteHeader.NAME); if(node == null) { node = checkRouteHeaderForSipNode(routeUri); } // SIPP sometimes appends more headers and lets remove them here. There is no legitimate reason // more than two SIP LB headers to be place next to each-other, so this cleanup is SAFE! boolean moreHeaders = true; while(moreHeaders) { RouteHeader extraHeader = (RouteHeader) request.getHeader(RouteHeader.NAME); if(extraHeader != null) { SipURI u = (SipURI)extraHeader.getAddress().getURI(); transport = ((ViaHeader) request.getHeader("Via")).getTransport(); if (u.getTransportParam() != null) transport = u.getTransportParam(); if (!isHeaderExternal(u.getHost(),u.getPort(), transport, isIpv6)) { numberOfRemovedRouteHeaders ++; request.removeFirst(RouteHeader.NAME); } else { moreHeaders = false; } } else { moreHeaders = false; } } } } } } if(node == null) { if(request.getRequestURI().isSipURI()) { node = checkRouteHeaderForSipNode((SipURI) request.getRequestURI()); } } //logger.info(request.ge + " has this hint " + node); ToHeader to = (ToHeader)(request.getHeader(ToHeader.NAME)); /* * We determine if this is subsequent based on To tag instead of checking route header metadata. */ boolean subsequent = to.getTag() != null; if(logger.isDebugEnabled()) { logger.debug("Number of removed Route headers is " + numberOfRemovedRouteHeaders); } if(numberOfRemovedRouteHeaders != 2 && subsequent) { logger.warn("A subsequent request should have two Route headers. Number of removed Route headers is " + numberOfRemovedRouteHeaders + ". This indicates a client is removing important headers."); } return new RouteHeaderHints(node, subsequent, callVersion); } /** * Check if the sip uri is meant for the LB same host and same port * @param sipUri sip Uri to check * @return */ private boolean isHeaderExternal(String host, int port,String transport, boolean isIpv6) { if(!isIpv6) { if (host.equalsIgnoreCase(balancerRunner.balancerContext.externalHost)) { if(balancerRunner.balancerContext.getExternalPortByTransport(transport,false)==port) return false; if(balancerRunner.balancerContext.getExternalLoadBalancerPortByTransport(transport,false)==port) return false; } if(host.equalsIgnoreCase(balancerRunner.balancerContext.internalHost)) { if(balancerRunner.balancerContext.getInternalPortByTransport(transport,false)==port) return false; if(balancerRunner.balancerContext.getInternalLoadBalancerPortByTransport(transport,false)==port) return false; } if(balancerRunner.balancerContext.externalIpLoadBalancerAddresses!=null&& balancerRunner.balancerContext.externalIpLoadBalancerAddresses.contains(host)) { if(balancerRunner.balancerContext.getExternalPortByTransport(transport,false)==port) return false; if(balancerRunner.balancerContext.getExternalLoadBalancerPortByTransport(transport,false)==port) return false; } if(balancerRunner.balancerContext.internalIpLoadBalancerAddresses!=null&& balancerRunner.balancerContext.internalIpLoadBalancerAddresses.contains(host)) { if(balancerRunner.balancerContext.getInternalPortByTransport(transport,false)==port) return false; if(balancerRunner.balancerContext.getInternalLoadBalancerPortByTransport(transport,false)==port) return false; } if(host.equalsIgnoreCase(balancerRunner.balancerContext.publicIP)) { if(balancerRunner.balancerContext.getExternalPortByTransport(transport,false)==port) return false; if(balancerRunner.balancerContext.getExternalLoadBalancerPortByTransport(transport,false)==port) return false; if(balancerRunner.balancerContext.getInternalPortByTransport(transport,false)==port) return false; if(balancerRunner.balancerContext.getInternalLoadBalancerPortByTransport(transport,false)==port) return false; } return true; } else { String cleanHost=host; if(cleanHost.startsWith("[")) cleanHost=cleanHost.substring(1); if(cleanHost.endsWith("]")) cleanHost=cleanHost.substring(0,cleanHost.length()-1); InetAddress address=null; try { address=InetAddress.getByName(cleanHost); } catch(UnknownHostException ex) { } if(address==null) return false; if (address.equals(balancerRunner.balancerContext.externalIpv6HostAddress)) { if(balancerRunner.balancerContext.getExternalPortByTransport(transport,true)==port) return false; if(balancerRunner.balancerContext.getExternalLoadBalancerPortByTransport(transport,true)==port) return false; } if(address.equals(balancerRunner.balancerContext.internalIpv6HostAddress)) { if(balancerRunner.balancerContext.getInternalPortByTransport(transport,true)==port) return false; if(balancerRunner.balancerContext.getInternalLoadBalancerPortByTransport(transport,true)==port) return false; } if(balancerRunner.balancerContext.externalIpv6LoadBalancerAddressHosts.contains(address)) { if(balancerRunner.balancerContext.getExternalPortByTransport(transport,true)==port) return false; if(balancerRunner.balancerContext.getExternalLoadBalancerPortByTransport(transport,true)==port) return false; } if(balancerRunner.balancerContext.internalIpv6LoadBalancerAddressHosts.contains(address)) { if(balancerRunner.balancerContext.getInternalPortByTransport(transport,true)==port) return false; if(balancerRunner.balancerContext.getInternalLoadBalancerPortByTransport(transport,true)==port) return false; } if(address.equals(balancerRunner.balancerContext.publicIPv6Host)) { if(balancerRunner.balancerContext.getExternalPortByTransport(transport,true)==port) return false; if(balancerRunner.balancerContext.getExternalLoadBalancerPortByTransport(transport,true)==port) return false; if(balancerRunner.balancerContext.getInternalPortByTransport(transport,true)==port) return false; if(balancerRunner.balancerContext.getInternalLoadBalancerPortByTransport(transport,true)==port) return false; } return true; } } /** * @param sipProvider * @param request * @throws InvalidArgumentException * @throws ParseException * @throws SipException */ private void decreaseMaxForwardsHeader(SipProvider sipProvider, Request request) throws InvalidArgumentException, ParseException, SipException { // Decreasing the Max Forward Header if(logger.isDebugEnabled()) { logger.debug("Decreasing the Max Forward Header "); } MaxForwardsHeader maxForwardsHeader = (MaxForwardsHeader) request.getHeader(MaxForwardsHeader.NAME); if (maxForwardsHeader == null) { maxForwardsHeader = balancerRunner.balancerContext.headerFactory.createMaxForwardsHeader(70); request.addHeader(maxForwardsHeader); } else { if(maxForwardsHeader.getMaxForwards() - 1 > 0) { maxForwardsHeader.setMaxForwards(maxForwardsHeader.getMaxForwards() - 1); } else { //Max forward header equals to 0, thus sending too many hops response Response response = balancerRunner.balancerContext.messageFactory.createResponse (Response.TOO_MANY_HOPS,request); sipProvider.sendResponse(response); } } } /** * @param originalRequest * @param serverTransaction * @throws ParseException * @throws SipException * @throws InvalidArgumentException * @throws TransactionUnavailableException */ /* * (non-Javadoc) * @see javax.sip.SipListener#processResponse(javax.sip.ResponseEvent) */ public void processResponse(ResponseEvent responseEvent) { BalancerAppContent content=(BalancerAppContent)responseEvent.getSource(); boolean isIpv6 = content.isIpv6(); SipProvider sipProvider = content.getProvider(); Response originalResponse = responseEvent.getResponse(); if(logger.isDebugEnabled()) { logger.debug("got response :\n" + originalResponse); } updateStats(originalResponse); final Response response = (Response) originalResponse; Node senderNode = getSenderNode(response); if(senderNode != null) { if(logger.isDebugEnabled()) { logger.debug("Updating Timestamp of sendernode: " + senderNode); } senderNode.updateTimerStamp(); } // Topmost via headers is me. As it is response to external request ViaHeader viaHeader = (ViaHeader) response.getHeader(ViaHeader.NAME); String branch = viaHeader.getBranch(); int versionDelimiter = branch.lastIndexOf('_'); String version = branch.substring(versionDelimiter + 1); InvocationContext ctx = balancerRunner.getInvocationContext(version); if(viaHeader!=null && !isHeaderExternal(viaHeader.getHost(), viaHeader.getPort(), viaHeader.getTransport(),isIpv6)) { response.removeFirst(ViaHeader.NAME); } viaHeader = (ViaHeader) response.getHeader(ViaHeader.NAME); String transport=viaHeader.getTransport(); if(viaHeader!=null && !isHeaderExternal(viaHeader.getHost(), viaHeader.getPort(), viaHeader.getTransport(),isIpv6)) { response.removeFirst(ViaHeader.NAME); } boolean fromServer = false; if(balancerRunner.balancerContext.isTwoEntrypoints()) { if(!isIpv6) fromServer = sipProvider.equals(balancerRunner.balancerContext.internalSipProvider); else fromServer = sipProvider.equals(balancerRunner.balancerContext.internalIpv6SipProvider); if(logger.isDebugEnabled()) { if(!isIpv6) logger.debug("fromServer : "+ fromServer + ", sipProvider " + sipProvider + ", internalSipProvider " + balancerRunner.balancerContext.internalSipProvider); else logger.debug("fromServer : "+ fromServer + ", sipProvider " + sipProvider + ", internalIpv6SipProvider " + balancerRunner.balancerContext.internalIpv6SipProvider); } } else { fromServer = senderNode == null; if(logger.isDebugEnabled()) { logger.debug("fromServer : "+ fromServer + ", senderNode " + senderNode); } } //removes rport and received from last Via header because of NEXMO patches it //only for external responses if(balancerRunner.balancerContext.isUseWithNexmo&&!fromServer) { viaHeader = (ViaHeader) response.getHeader(ViaHeader.NAME); if(viaHeader!=null) { if(logger.isDebugEnabled()) logger.debug("We are going to remove rport and received parametres from :" + viaHeader + " from external response"); response.removeFirst(ViaHeader.NAME); viaHeader.removeParameter("rport"); viaHeader.removeParameter("received"); try { response.addFirst(viaHeader); } catch (NullPointerException | SipException e) { e.printStackTrace(); } if(logger.isDebugEnabled()) logger.debug("After removing :" + response); } } if(fromServer) { if(senderNode!=null&&senderNode.getIp()!=null) mediaFailureDetection(response, ctx, senderNode); /* if("true".equals(balancerRunner.balancerContext.properties.getProperty("removeNodesOn500Response")) && response.getStatusCode() == 500) { // If the server is broken remove it from the list and try another one with the next retransmission if(!(sourceNode instanceof ExtraServerNode)) { if(balancerRunner.balancerContext.nodes.size()>1) { balancerRunner.balancerContext.nodes.remove(sourceNode); balancerRunner.balancerContext.balancerAlgorithm.nodeRemoved(sourceNode); } } } */ String publicIp = null; if(!isIpv6) publicIp = balancerRunner.balancerContext.publicIP; else publicIp = balancerRunner.balancerContext.publicIPv6; if(publicIp != null && publicIp.trim().length() > 0) { if(logger.isDebugEnabled()) { logger.debug("Will add Record-Route header to response with public IP Address: "+publicIp); } patchSipMessageForNAT(response, isIpv6); } // https://github.com/RestComm/load-balancer/issues/45 Adding sender node for the algorithm to be available ((ResponseExt)response).setApplicationData(senderNode); ctx.balancerAlgorithm.processInternalResponse(response,isIpv6); try { if(logger.isDebugEnabled()) { logger.debug("from server sending response externally " + response); } if(!isIpv6) balancerRunner.balancerContext.externalSipProvider.sendResponse(response); else balancerRunner.balancerContext.externalIpv6SipProvider.sendResponse(response); } catch (Exception ex) { logger.error("Unexpected exception while forwarding the response \n" + response, ex); } } else { try { SIPMessage message = (SIPMessage)response; String initialRemoteAddr = message.getPeerPacketSourceAddress().getHostAddress(); String initialRemotePort = String.valueOf(message.getPeerPacketSourcePort()); Header remoteAddrHeader = null; Header remotePortHeader = null; try { HeaderFactory hf=SipFactory.getInstance().createHeaderFactory(); remoteAddrHeader = hf.createHeader("X-Sip-Balancer-InitialRemoteAddr", initialRemoteAddr); remotePortHeader = hf.createHeader("X-Sip-Balancer-InitialRemotePort", initialRemotePort); } catch (PeerUnavailableException e) { logger.error("Unexpected exception while creating custom headers for REGISTER message ", e); } catch (ParseException e) { logger.error("Unexpected exception while creating custom headers for REGISTER message ", e); } if (remoteAddrHeader != null) response.addHeader(remoteAddrHeader); if (remotePortHeader != null) response.addHeader(remotePortHeader); if(balancerRunner.balancerContext.isTwoEntrypoints()) { ctx.balancerAlgorithm.processExternalResponse(response,isIpv6); if(logger.isDebugEnabled()) { logger.debug("two entry points: from external sending response " + response); } if(!isIpv6) balancerRunner.balancerContext.internalSipProvider.sendResponse(response); else balancerRunner.balancerContext.internalIpv6SipProvider.sendResponse(response); } else { if(!comesFromInternalNode(response,ctx,initialRemoteAddr,message.getPeerPacketSourcePort(),transport,isIpv6)) ctx.balancerAlgorithm.processExternalResponse(response,isIpv6); else ctx.balancerAlgorithm.processInternalResponse(response,isIpv6); if(logger.isDebugEnabled()) { logger.debug("one entry point: from external sending response " + response); } if(!isIpv6) balancerRunner.balancerContext.externalSipProvider.sendResponse(response); else balancerRunner.balancerContext.externalIpv6SipProvider.sendResponse(response); } } catch (Exception ex) { logger.error("Unexpected exception while forwarding the response \n" + response, ex); } } } private void mediaFailureDetection(Response response, InvocationContext ctx, Node node) { Boolean isIpV6=LbUtils.isValidInet6Address(node.getIp()); KeySip keySip = new KeySip(node,isIpV6); if(balancerRunner.balancerContext.responsesStatusCodeNodeRemoval.contains(response.getStatusCode())) // adding null check for https://github.com/RestComm/load-balancer/issues/83 if(ctx.sipNodeMap(isIpV6).get(keySip) != null && ctx.sipNodeMap(isIpV6).get(keySip).getAndIncrementFailCounter()>2) { logger.error("mediaFailureDetection on keysip " + keySip + ", removing node " + node); ctx.sipNodeMap(isIpV6).remove(keySip); ctx.balancerAlgorithm.nodeRemoved(node); } } //need to verify that comes from external in case of single leg protected Boolean comesFromInternalNode(Response externalResponse,InvocationContext ctx,String host,Integer port,String transport,Boolean isIpV6) { boolean found = false; if(host!=null && port!=null) { if(ctx.sipNodeMap(isIpV6).containsKey(new KeySip(host, port,isIpV6))) found = true; // for(Node node : ctx.nodes) { // if(node.getIp().equals(host)) { // if(port.equals(node.getProperties().get(transport+"Port"))) { // found = true; // break; // } // } // } } return found; } /** * Patch Response for NAT Environment where the Load Balancer runs on a private IP but UAs need to know the LB public IP to * send back subsequent requests to it. * @param sipMessage the response to patch * @throws PeerUnavailableException * @throws ParseException */ public void patchSipMessageForNAT(javax.sip.message.Message sipMessage, boolean isIpv6) { //Need to patch the response so the subsequent requests are directly correctly at the public IP Address of the LB // Useful for NAT environment such as Amazon EC2 String currentPublicIp = null; if(!isIpv6) currentPublicIp = balancerRunner.balancerContext.publicIP; else currentPublicIp = balancerRunner.balancerContext.publicIPv6; if (currentPublicIp != null && !currentPublicIp.isEmpty()) { int [] ports = new int[5]; // int udpPort = 0; // int tcpPort = 0; // int tlsPort = 0; // int wsPort = 0; // int wssPort = 0; if(!isIpv6) { for(int i = 0; i < 5; i++) ports[i] = balancerRunner.balancerContext.externalPorts[i]; // udpPort = balancerRunner.balancerContext.externalUdpPort; // tcpPort = balancerRunner.balancerContext.externalTcpPort; // tlsPort = balancerRunner.balancerContext.externalTlsPort; // wsPort = balancerRunner.balancerContext.externalWsPort; // wssPort = balancerRunner.balancerContext.externalWssPort; } else { for(int i = 0; i < 5; i++) ports[i] = balancerRunner.balancerContext.externalIpv6Ports[i]; // udpPort = balancerRunner.balancerContext.externalIpv6UdpPort; // tcpPort = balancerRunner.balancerContext.externalIpv6TcpPort; // tlsPort = balancerRunner.balancerContext.externalIpv6TlsPort; // wsPort = balancerRunner.balancerContext.externalIpv6WsPort; // wssPort = balancerRunner.balancerContext.externalIpv6WssPort; } String transport = null; if (sipMessage instanceof Response) transport = ((ViaHeader) sipMessage.getHeader(ViaHeader.NAME)).getTransport().toLowerCase(); else { URI requestURI = ((Request) sipMessage).getRequestURI(); transport = ((ViaHeader) sipMessage.getHeader(ViaHeader.NAME)).getTransport().toLowerCase(); if (requestURI.isSipURI()) { if (((SipUri) requestURI).getTransportParam() != null) transport = ((SipUri) requestURI).getTransportParam(); } } String privateIp = balancerRunner.balancerContext.host; @SuppressWarnings("unchecked") ListIterator<RecordRouteHeader> recordRouteHeaderList = sipMessage.getHeaders(RecordRouteHeader.NAME); try { HeaderFactory headerFactory = SipFactory.getInstance().createHeaderFactory(); Header contactHeader = null; if (!recordRouteHeaderList.hasNext()) { if(logger.isDebugEnabled()) { logger.debug("Record Route header list is empty"); } } while (recordRouteHeaderList.hasNext()) { RecordRouteHeader recordRouteHeader = (RecordRouteHeader) recordRouteHeaderList.next(); if(logger.isDebugEnabled()) { logger.debug("About to check Record-Route header: "+recordRouteHeader.toString()); } if (((SipURI)recordRouteHeader.getAddress().getURI()).getHost().equals(privateIp)) { //If this RecordRoute header is from LB for(int port : ports) if(((SipURI)recordRouteHeader.getAddress().getURI()).getPort()==port) { // And if the port is the external Port SipURI sipURI = (SipURI) recordRouteHeader.getAddress().getURI(); sipURI.setHost(currentPublicIp); } } else { if(logger.isDebugEnabled()) { logger.debug("Didn't patched the Record-Route because ip address is not the private one: "+((SipURI)recordRouteHeader.getAddress().getURI()).getHost()); } } } if (sipMessage.getHeader(ContactHeader.NAME) != null) { final String displayedName = ((ContactHeader)sipMessage.getHeader("Contact")).getAddress().getDisplayName(); int currPort=balancerRunner.balancerContext.getExternalPortByTransport(transport,isIpv6); if (displayedName != null && !displayedName.isEmpty()) { final String contactURI = "sip:"+displayedName+"@"+currentPublicIp+":"+currPort; contactHeader = headerFactory.createHeader("Contact", contactURI); ((ContactHeader)contactHeader).getAddress().setDisplayName(displayedName); } else { final String contactURI = "sip:"+currentPublicIp+":"+currPort; contactHeader = headerFactory.createHeader("Contact", contactURI); } if (contactHeader != null) { sipMessage.removeFirst("Contact"); sipMessage.addHeader(contactHeader); } } if(logger.isDebugEnabled() && contactHeader != null) { logger.debug("Patched the Contact header with : "+contactHeader.toString()); } } catch (PeerUnavailableException peerUnavailableException) { logger.error("Unexpected exception while forwarding the response \n" + sipMessage, peerUnavailableException); } catch (ParseException parseException) { logger.error("Unexpected exception while forwarding the response \n" + sipMessage, parseException); } catch (NullPointerException e) { logger.error("Unexpected exception while forwarding the response \n" + sipMessage, e); } } } /* * (non-Javadoc) * @see javax.sip.SipListener#processTimeout(javax.sip.TimeoutEvent) */ public void processTimeout(TimeoutEvent timeoutEvent) { Transaction transaction = null; if(timeoutEvent.isServerTransaction()) { transaction = timeoutEvent.getServerTransaction(); if(logger.isDebugEnabled()) { logger.debug("timeout => " + transaction.getRequest().toString()); } } else { transaction = timeoutEvent.getClientTransaction(); if(logger.isDebugEnabled()) { logger.debug("timeout => " + transaction.getRequest().toString()); } } String callId = ((CallIdHeader)transaction.getRequest().getHeader(CallIdHeader.NAME)).getCallId(); register.unStickSessionFromNode(callId); } /* * (non-Javadoc) * @see javax.sip.SipListener#processTransactionTerminated(javax.sip.TransactionTerminatedEvent) */ public void processTransactionTerminated( TransactionTerminatedEvent transactionTerminatedEvent) { Transaction transaction = null; if(transactionTerminatedEvent.isServerTransaction()) { transaction = transactionTerminatedEvent.getServerTransaction(); if(logger.isDebugEnabled()) { logger.debug("timeout => " + transaction.getRequest().toString()); } } else { transaction = transactionTerminatedEvent.getClientTransaction(); if(logger.isDebugEnabled()) { logger.debug("timeout => " + transaction.getRequest().toString()); } } if(Request.BYE.equals(transaction.getRequest().getMethod())) { String callId = ((CallIdHeader)transaction.getRequest().getHeader(CallIdHeader.NAME)).getCallId(); register.unStickSessionFromNode(callId); } } /** * @return the requestsProcessed */ public long getNumberOfRequestsProcessed() { return balancerRunner.balancerContext.requestsProcessed.get(); } /** * @return the responsesProcessed */ public long getNumberOfResponsesProcessed() { return balancerRunner.balancerContext.responsesProcessed.get(); } /** * @return the bytesTransfered */ public long getNumberOfBytesTransferred() { return balancerRunner.balancerContext.bytesTransferred.get(); } /** * @return the requestsProcessedByMethod */ public long getRequestsProcessedByMethod(String method) { AtomicLong requestsProcessed = balancerRunner.balancerContext.requestsProcessedByMethod.get(method); if(requestsProcessed != null) { return requestsProcessed.get(); } return 0; } /** * @return the responsesProcessedByStatusCode */ public long getResponsesProcessedByStatusCode(String statusCode) { AtomicLong responsesProcessed = balancerRunner.balancerContext.responsesProcessedByStatusCode.get(statusCode); if(responsesProcessed != null) { return responsesProcessed.get(); } return 0; } public Map<String, AtomicLong> getNumberOfRequestsProcessedByMethod() { return balancerRunner.balancerContext.requestsProcessedByMethod; } public Map<String, AtomicLong> getNumberOfResponsesProcessedByStatusCode() { return balancerRunner.balancerContext.responsesProcessedByStatusCode; } public BalancerContext getBalancerAlgorithmContext() { return balancerRunner.balancerContext; } public void setBalancerAlgorithmContext( BalancerContext balancerAlgorithmContext) { balancerRunner.balancerContext = balancerAlgorithmContext; } /** * @param skipStatistics the skipStatistics to set */ public void setGatherStatistics(boolean skipStatistics) { balancerRunner.balancerContext.gatherStatistics = skipStatistics; } /** * @return the skipStatistics */ public boolean isGatherStatistics() { return balancerRunner.balancerContext.gatherStatistics; } private String getTransportById(int i) { String transport = null; switch (i) { case 0: transport = ListeningPoint.UDP; break; case 1: transport = ListeningPoint.TCP; break; case 2: transport = ListeningPoint.TLS; break; case 3: transport = ListeningPointExt.WS; break; case 4: transport = ListeningPointExt.WSS; break; } return transport; } private int getIndexByTransport(String transport) { int index = 0; switch (transport) { case ListeningPoint.UDP: index = UDP; break; case ListeningPoint.TCP: index = TCP; break; case ListeningPoint.TLS: index = TLS; break; case ListeningPointExt.WS: index = WS; break; case ListeningPointExt.WSS: index = WSS; break; } return index; } private void setLoadBalacerHostsPorts() { balancerRunner.balancerContext.host = balancerRunner.balancerContext.lbConfig.getCommonConfiguration().getHost(); balancerRunner.balancerContext.internalHost = (balancerRunner.balancerContext.lbConfig.getSipConfiguration().getInternalLegConfiguration().getHost()==null|| balancerRunner.balancerContext.lbConfig.getSipConfiguration().getInternalLegConfiguration().getHost().equals(""))? balancerRunner.balancerContext.host:balancerRunner.balancerContext.lbConfig.getSipConfiguration().getInternalLegConfiguration().getHost(); balancerRunner.balancerContext.externalHost = (balancerRunner.balancerContext.lbConfig.getSipConfiguration().getExternalLegConfiguration().getHost()==null|| balancerRunner.balancerContext.lbConfig.getSipConfiguration().getExternalLegConfiguration().getHost().equals(""))? balancerRunner.balancerContext.host:balancerRunner.balancerContext.lbConfig.getSipConfiguration().getExternalLegConfiguration().getHost(); balancerRunner.balancerContext.ipv6Host = balancerRunner.balancerContext.lbConfig.getCommonConfiguration().getIpv6Host(); balancerRunner.balancerContext.externalIpv6Host = (balancerRunner.balancerContext.lbConfig.getSipConfiguration().getExternalLegConfiguration().getIpv6Host()==null|| balancerRunner.balancerContext.lbConfig.getSipConfiguration().getExternalLegConfiguration().getIpv6Host().equals(""))? balancerRunner.balancerContext.ipv6Host:balancerRunner.balancerContext.lbConfig.getSipConfiguration().getExternalLegConfiguration().getIpv6Host(); balancerRunner.balancerContext.internalIpv6Host = (balancerRunner.balancerContext.lbConfig.getSipConfiguration().getInternalLegConfiguration().getIpv6Host()==null|| balancerRunner.balancerContext.lbConfig.getSipConfiguration().getInternalLegConfiguration().getIpv6Host().equals(""))? balancerRunner.balancerContext.ipv6Host:balancerRunner.balancerContext.lbConfig.getSipConfiguration().getInternalLegConfiguration().getIpv6Host(); if(balancerRunner.balancerContext.externalIpv6Host!=null) { try { balancerRunner.balancerContext.externalIpv6HostAddress=InetAddress.getByName(balancerRunner.balancerContext.externalIpv6Host); } catch(UnknownHostException ex) { } } if(balancerRunner.balancerContext.internalIpv6Host!=null) { try { balancerRunner.balancerContext.internalIpv6HostAddress=InetAddress.getByName(balancerRunner.balancerContext.internalIpv6Host); } catch(UnknownHostException ex) { } } balancerRunner.balancerContext.externalPorts = balancerRunner.balancerContext.lbConfig.getSipConfiguration().getExternalLegConfiguration().getPorts(); balancerRunner.balancerContext.externalIpv6Ports = balancerRunner.balancerContext.lbConfig.getSipConfiguration().getExternalLegConfiguration().getIpv6Ports(); balancerRunner.balancerContext.internalPorts = balancerRunner.balancerContext.lbConfig.getSipConfiguration().getInternalLegConfiguration().getPorts(); balancerRunner.balancerContext.internalIpv6Ports = balancerRunner.balancerContext.lbConfig.getSipConfiguration().getInternalLegConfiguration().getIpv6Ports(); balancerRunner.balancerContext.externalIpLoadBalancerAddresses = balancerRunner.balancerContext.lbConfig.getSipConfiguration().getExternalLegConfiguration().getIpLoadBalancerAddress(); balancerRunner.balancerContext.internalIpLoadBalancerAddresses = balancerRunner.balancerContext.lbConfig.getSipConfiguration().getInternalLegConfiguration().getIpLoadBalancerAddress(); balancerRunner.balancerContext.externalIpv6LoadBalancerAddresses = balancerRunner.balancerContext.lbConfig.getSipConfiguration().getExternalLegConfiguration().getIpv6LoadBalancerAddress(); balancerRunner.balancerContext.internalIpv6LoadBalancerAddresses = balancerRunner.balancerContext.lbConfig.getSipConfiguration().getInternalLegConfiguration().getIpv6LoadBalancerAddress(); if(balancerRunner.balancerContext.externalIpv6LoadBalancerAddresses!=null) { try { for(String externalIpv6LoadBalancerAddress : balancerRunner.balancerContext.externalIpv6LoadBalancerAddresses) balancerRunner.balancerContext.externalIpv6LoadBalancerAddressHosts.add(InetAddress.getByName(externalIpv6LoadBalancerAddress)); } catch(UnknownHostException ex) { } } if(balancerRunner.balancerContext.internalIpv6LoadBalancerAddresses!=null) { try { for(String internalIpv6LoadBalancerAddress : balancerRunner.balancerContext.internalIpv6LoadBalancerAddresses) balancerRunner.balancerContext.internalIpv6LoadBalancerAddressHosts.add(InetAddress.getByName(internalIpv6LoadBalancerAddress)); } catch(UnknownHostException ex) { } } balancerRunner.balancerContext.externalIpLoadBalancerPorts = balancerRunner.balancerContext.lbConfig.getSipConfiguration().getExternalLegConfiguration().getIPLoadBalancerPorts(); balancerRunner.balancerContext.externalIpv6LoadBalancerPorts = balancerRunner.balancerContext.lbConfig.getSipConfiguration().getExternalLegConfiguration().getIpv6LoadBalancerPorts(); balancerRunner.balancerContext.internalIpLoadBalancerPorts = balancerRunner.balancerContext.lbConfig.getSipConfiguration().getInternalLegConfiguration().getIPLoadBalancerPorts(); balancerRunner.balancerContext.internalIpv6LoadBalancerPorts = balancerRunner.balancerContext.lbConfig.getSipConfiguration().getInternalLegConfiguration().getIpv6LoadBalancerPorts(); // https://github.com/RestComm/load-balancer/issues/43 don't use host by default if public-ip is not set or it will result in contact header responses being badly patched balancerRunner.balancerContext.publicIP = balancerRunner.balancerContext.lbConfig.getSipConfiguration().getPublicIp(); balancerRunner.balancerContext.publicIPv6 = balancerRunner.balancerContext.lbConfig.getSipConfiguration().getPublicIpv6(); if(balancerRunner.balancerContext.publicIPv6!=null&&!balancerRunner.balancerContext.publicIPv6.equals("")) { try { balancerRunner.balancerContext.publicIPv6Host=InetAddress.getByName(balancerRunner.balancerContext.publicIPv6); } catch(UnknownHostException ex) { } } //checking of settings if(balancerRunner.balancerContext.isTwoEntrypoints()) { for(int i = 0; i < 5; i++) if(balancerRunner.balancerContext.externalIpLoadBalancerPorts[i] > 0) { if(balancerRunner.balancerContext.internalIpLoadBalancerPorts[i] <=0) { throw new RuntimeException("External IP load balancer specified "+getTransportById(i)+" port, but not internal load balancer "+getTransportById(i)+" port"); } } for(int i = 0; i < 5; i++) if(balancerRunner.balancerContext.externalIpv6LoadBalancerPorts[i] > 0) { if(balancerRunner.balancerContext.internalIpv6LoadBalancerPorts[i] <= 0) { throw new RuntimeException("External IP load balancer specified "+getTransportById(i)+" port, but not internal load balancer "+getTransportById(i)+" port"); } } } int [] emptyArray = new int[5]; if(balancerRunner.balancerContext.externalIpLoadBalancerAddresses != null) if(Arrays.equals(balancerRunner.balancerContext.externalIpLoadBalancerPorts,emptyArray)) throw new RuntimeException("External load balancer address is specified, but none externalIpLoadBalancerPort "); if(balancerRunner.balancerContext.externalIpv6LoadBalancerAddresses != null) if(Arrays.equals(balancerRunner.balancerContext.externalIpv6LoadBalancerPorts,emptyArray)) throw new RuntimeException("External load balancer ipv6 address is specified, but none externalIpv6LoadBalancerPort "); if(balancerRunner.balancerContext.internalIpLoadBalancerAddresses != null) if(Arrays.equals(balancerRunner.balancerContext.internalIpLoadBalancerPorts,emptyArray)) throw new RuntimeException("Internal load balancer address is specified, but none internalIpLoadBalancerPort"); if(balancerRunner.balancerContext.internalIpv6LoadBalancerAddresses != null) if(Arrays.equals(balancerRunner.balancerContext.internalIpv6LoadBalancerPorts,emptyArray)) throw new RuntimeException("Internal load balancer ipv6 address is specified, but none internalIpv6LoadBalancerPort"); } private void setExtraServerNodes(String extraServerNodesString) { ArrayList<Node> extraServerNodes = new ArrayList<Node>(); extraServerAddresses = extraServerNodesString.split(","); extraServerPorts = new int[extraServerAddresses.length]; for(int q=0; q<extraServerAddresses.length; q++) { int indexOfPort = extraServerAddresses[q].indexOf(':'); if(indexOfPort > 0) { extraServerPorts[q] = Integer.parseInt(extraServerAddresses[q].substring(indexOfPort + 1, extraServerAddresses[q].length())); extraServerAddresses[q] = extraServerAddresses[q].substring(0, indexOfPort); } else { extraServerPorts[q] = 5060; } ExtraServerNode extraServerNode = new ExtraServerNode("ExtraServerNode"+q+"-"+extraServerAddresses[q]+":"+extraServerPorts[q], extraServerAddresses[q]); HashMap<String, String> properties = new HashMap<String, String>(); properties.put("udpPort",""+ extraServerPorts[q]); properties.put("tcpPort",""+ extraServerPorts[q]); properties.put("version","0"); extraServerNode.setProperties(properties); extraServerNodes.add(extraServerNode); logger.info("Extra Server: " + extraServerAddresses[q] + ":" + extraServerPorts[q]); } if(balancerRunner.balancerContext.lbConfig.getSipConfiguration().isPerformanceTestingMode()){ register.handlePingInRegister(extraServerNodes); logger.info("Extra Servers registered as active nodes!"); } } private void createRecordRouteHeaders(ArrayList<ListeningPoint> listeningPoints, RecordRouteHeader[] activeHeaders) throws ParseException { for (ListeningPoint listeningPoint : listeningPoints) { SipURI internalLocalUri = balancerRunner.balancerContext.addressFactory.createSipURI(null, listeningPoint.getIPAddress()); internalLocalUri.setPort(listeningPoint.getPort()); internalLocalUri.setTransportParam(listeningPoint.getTransport()); //See RFC 3261 19.1.1 for lr parameter internalLocalUri.setLrParam(); Address internalLocalAddress = balancerRunner.balancerContext.addressFactory.createAddress(internalLocalUri); internalLocalAddress.setURI(internalLocalUri); if(logger.isDebugEnabled()) { logger.debug("adding internal Record Router Header :"+internalLocalAddress); } activeHeaders[getIndexByTransport(listeningPoint.getTransport())] = balancerRunner.balancerContext.headerFactory.createRecordRouteHeader(internalLocalAddress); } } private void createRecordRouteHeaders(int[] ports, String address, RecordRouteHeader[] activeHeaders) throws ParseException { for(int i = 0; i < 5; i++) if(ports[i] > 0) { SipURI ipLbSipUri = balancerRunner.balancerContext.addressFactory.createSipURI(null, address); ipLbSipUri.setPort(ports[i]); ipLbSipUri.setTransportParam(getTransportById(i)); ipLbSipUri.setLrParam(); Address ipLbAdress = balancerRunner.balancerContext.addressFactory.createAddress(ipLbSipUri); ipLbAdress.setURI(ipLbSipUri); activeHeaders[i] = balancerRunner.balancerContext.headerFactory.createRecordRouteHeader(ipLbAdress); } } private void printConfigInfo() { StringBuilder portsInfo = new StringBuilder("Sip Balancer started on external address " + balancerRunner.balancerContext.externalHost); for(int i = 0; i < 5; i++) portsInfo.append(", external "+getTransportById(i)+" port : " + balancerRunner.balancerContext.externalPorts[i]); for(int i = 0; i < 5; i++) portsInfo.append(", internal "+getTransportById(i)+" port : " + balancerRunner.balancerContext.internalPorts[i]); logger.info(portsInfo); if(balancerRunner.balancerContext.externalIpv6Host!=null) { portsInfo = new StringBuilder("Sip Balancer started on external IPv6 address " + balancerRunner.balancerContext.externalIpv6Host); for(int i = 0; i < 5; i++) portsInfo.append(", external ipv6 "+getTransportById(i)+" port : " + balancerRunner.balancerContext.externalIpv6Ports[i]); for(int i = 0; i < 5; i++) portsInfo.append(", internal ipv6 "+getTransportById(i)+" port : " + balancerRunner.balancerContext.internalIpv6Ports[i]); logger.info(portsInfo); } } private void setSipLoadBalancerProperty() { balancerRunner.balancerContext.isSendTrying = balancerRunner.balancerContext.lbConfig.getSipConfiguration().isSendTrying(); balancerRunner.balancerContext.isSend5xxResponse = balancerRunner.balancerContext.lbConfig.getSipConfiguration().getIsSend5xxResponse(); balancerRunner.balancerContext.isSend5xxResponseReasonHeader = balancerRunner.balancerContext.lbConfig.getSipConfiguration().getIsSend5xxResponseReasonHeader(); balancerRunner.balancerContext.isSend5xxResponseSatusCode = balancerRunner.balancerContext.lbConfig.getSipConfiguration().getIsSend5xxResponseSatusCode(); balancerRunner.balancerContext.sipHeaderAffinityKey = balancerRunner.balancerContext.lbConfig.getSipConfiguration().getAlgorithmConfiguration().getSipHeaderAffinityKey(); balancerRunner.balancerContext.sipHeaderAffinityFallbackKey = balancerRunner.balancerContext.lbConfig.getSipConfiguration().getAlgorithmConfiguration().getSipHeaderAffinityFallbackKey(); balancerRunner.balancerContext.sipHeaderAffinityKeyExclusionPattern = null; if(balancerRunner.balancerContext.lbConfig.getSipConfiguration().getAlgorithmConfiguration().getSipHeaderAffinityKeyExclusionPattern() != null && !balancerRunner.balancerContext.lbConfig.getSipConfiguration().getAlgorithmConfiguration().getSipHeaderAffinityKeyExclusionPattern().trim().isEmpty()) { balancerRunner.balancerContext.sipHeaderAffinityKeyExclusionPattern = Pattern.compile(balancerRunner.balancerContext.lbConfig.getSipConfiguration().getAlgorithmConfiguration().getSipHeaderAffinityKeyExclusionPattern()); } balancerRunner.balancerContext.isUseWithNexmo = balancerRunner.balancerContext.lbConfig.getSipConfiguration().getIsUseWithNexmo(); balancerRunner.balancerContext.responsesStatusCodeNodeRemoval = balancerRunner.balancerContext.lbConfig.getSipConfiguration().getResponsesStatusCodeNodeRemoval(); balancerRunner.balancerContext.matchingHostnameForRoute = balancerRunner.balancerContext.lbConfig.getSipConfiguration().getMatchingHostnameForRoute(); balancerRunner.balancerContext.isFilterSubdomain = balancerRunner.balancerContext.lbConfig.getSipConfiguration().getIsFilterSubdomain(); balancerRunner.balancerContext.internalTransport = balancerRunner.balancerContext.lbConfig.getSipConfiguration().getInternalTransport(); balancerRunner.balancerContext.blockedList = new ArrayList<String>(Arrays.asList(balancerRunner.balancerContext.lbConfig.getSipConfiguration().getBlockedValues().split(","))); } private void setViaHostsPorts() { if (balancerRunner.balancerContext.lbConfig.getSipConfiguration().isUseIpLoadBalancerAddressInViaHeaders()) { if(balancerRunner.balancerContext.externalIpLoadBalancerAddresses!=null) balancerRunner.balancerContext.externalViaHost = balancerRunner.balancerContext.externalIpLoadBalancerAddresses.get(0); if(balancerRunner.balancerContext.internalIpLoadBalancerAddresses!=null) balancerRunner.balancerContext.internalViaHost = balancerRunner.balancerContext.internalIpLoadBalancerAddresses.get(0); balancerRunner.balancerContext.externalViaPorts = balancerRunner.balancerContext.externalIpLoadBalancerPorts; balancerRunner.balancerContext.internalViaPorts = balancerRunner.balancerContext.internalIpLoadBalancerPorts; if(balancerRunner.balancerContext.externalIpv6LoadBalancerAddresses!=null) balancerRunner.balancerContext.externalIpv6ViaHost = balancerRunner.balancerContext.externalIpv6LoadBalancerAddresses.get(0); if(balancerRunner.balancerContext.internalIpv6LoadBalancerAddresses!=null) balancerRunner.balancerContext.internalIpv6ViaHost = balancerRunner.balancerContext.internalIpv6LoadBalancerAddresses.get(0); balancerRunner.balancerContext.externalIpv6ViaPorts = balancerRunner.balancerContext.externalIpv6LoadBalancerPorts; balancerRunner.balancerContext.internalIpv6ViaPorts = balancerRunner.balancerContext.internalIpv6LoadBalancerPorts; } else { balancerRunner.balancerContext.externalViaHost = balancerRunner.balancerContext.externalHost; balancerRunner.balancerContext.internalViaHost = balancerRunner.balancerContext.internalHost; balancerRunner.balancerContext.externalViaPorts = balancerRunner.balancerContext.externalPorts; balancerRunner.balancerContext.internalViaPorts = balancerRunner.balancerContext.internalPorts; balancerRunner.balancerContext.externalIpv6ViaHost = balancerRunner.balancerContext.externalIpv6Host; balancerRunner.balancerContext.internalIpv6ViaHost = balancerRunner.balancerContext.internalIpv6Host; balancerRunner.balancerContext.externalIpv6ViaPorts = balancerRunner.balancerContext.externalIpv6Ports; balancerRunner.balancerContext.internalIpv6ViaPorts = balancerRunner.balancerContext.internalIpv6Ports; } } private void createSipStack() throws PeerUnavailableException { SipFactory sipFactory = null; try { sipFactory = SipFactory.getInstance(); String pathName = balancerRunner.balancerContext.lbConfig.getSipStackConfiguration().getSipStackProperies().getProperty("pathName", "org.mobicents.ext"); logger.info("SIP Stack pathName " + pathName); sipFactory.setPathName(pathName); balancerRunner.balancerContext.sipStack = (SipStackImpl) sipFactory.createSipStack(balancerRunner.balancerContext.lbConfig.getSipStackConfiguration().getSipStackProperies()); } catch (PeerUnavailableException pue) { throw new IllegalStateException("Cant create stack due to["+pue.getMessage()+"]", pue); } balancerRunner.balancerContext.headerFactory = sipFactory.createHeaderFactory(); if(balancerRunner.balancerContext.lbConfig.getSipConfiguration().isUsePrettyEncoding()) ((HeaderFactoryImpl)balancerRunner.balancerContext.headerFactory).setPrettyEncoding(true); balancerRunner.balancerContext.addressFactory = sipFactory.createAddressFactory(); balancerRunner.balancerContext.messageFactory = sipFactory.createMessageFactory(); } private SipProvider createSipProviders(ArrayList<ListeningPoint> lps) throws ObjectInUseException, TransportAlreadySupportedException, TooManyListenersException { SipProvider sipProvider = balancerRunner.balancerContext.sipStack.createSipProvider(lps.remove(0)); for (ListeningPoint lp : lps) sipProvider.addListeningPoint(lp); sipProvider.addSipListener(this); return sipProvider; } private void createListeningPoints(int [] ports, String host, ArrayList<ListeningPoint> lps) throws TransportNotSupportedException, InvalidArgumentException { for(int i = 0; i < 5 ; i++) if(ports[i] > 0) lps.add(balancerRunner.balancerContext.sipStack.createListeningPoint(host, ports[i], getTransportById(i))); } }