All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.mobius.software.telco.protocols.diameter.impl.MessageProcessingTask Maven / Gradle / Ivy

There is a newer version: 10.0.0-18-java11
Show newest version
package com.mobius.software.telco.protocols.diameter.impl;
/*
 * Mobius Software LTD
 * Copyright 2019 - 2023, Mobius Software LTD and individual contributors
 * by the @authors tag.
 *
 * This program is free software: you can redistribute it and/or modify
 * under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation; either version 3 of
 * the License, or (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see 
 */

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.restcomm.protocols.api.Association;

import com.mobius.software.common.dal.timers.Task;
import com.mobius.software.telco.protocols.diameter.AsyncCallback;
import com.mobius.software.telco.protocols.diameter.DiameterAnswerData;
import com.mobius.software.telco.protocols.diameter.DiameterLink;
import com.mobius.software.telco.protocols.diameter.DiameterProvider;
import com.mobius.software.telco.protocols.diameter.DiameterStack;
import com.mobius.software.telco.protocols.diameter.NetworkListener;
import com.mobius.software.telco.protocols.diameter.PeerStateEnum;
import com.mobius.software.telco.protocols.diameter.ResultCodes;
import com.mobius.software.telco.protocols.diameter.annotations.DiameterCommandDefinition;
import com.mobius.software.telco.protocols.diameter.commands.DiameterMessage;
import com.mobius.software.telco.protocols.diameter.commands.DiameterRequest;
import com.mobius.software.telco.protocols.diameter.commands.commons.AccountingAnswer;
import com.mobius.software.telco.protocols.diameter.commands.commons.AccountingRequest;
import com.mobius.software.telco.protocols.diameter.commands.commons.CapabilitiesExchangeAnswer;
import com.mobius.software.telco.protocols.diameter.commands.commons.CapabilitiesExchangeRequest;
import com.mobius.software.telco.protocols.diameter.commands.commons.DeviceWatchdogAnswer;
import com.mobius.software.telco.protocols.diameter.commands.commons.DeviceWatchdogRequest;
import com.mobius.software.telco.protocols.diameter.commands.commons.DisconnectPeerAnswer;
import com.mobius.software.telco.protocols.diameter.commands.commons.DisconnectPeerRequest;
import com.mobius.software.telco.protocols.diameter.exceptions.DiameterException;
import com.mobius.software.telco.protocols.diameter.parser.DiameterParser;
import com.mobius.software.telco.protocols.diameter.primitives.common.VendorSpecificApplicationId;

import io.netty.buffer.ByteBuf;

/**
*
* @author yulian oifa
*
*/
public class MessageProcessingTask implements Task
{
	public static Logger logger=LogManager.getLogger(DiameterLinkImpl.class);
	
	private ByteBuf buffer;
	private DiameterMessage message;
	private DiameterStack stack;
	private DiameterLink link;	
	private Association association;
	private AtomicReference> remoteApplicationIds;
	private AtomicReference> remoteAuthApplicationIds;
	private AtomicReference> remoteAcctApplicationIds;
	private AtomicLong lastActivity;
	private AtomicBoolean waitingForDWA;
	private Long startTime = System.currentTimeMillis();
	private ConcurrentHashMap genericListeners;
	
	private AsyncCallback dummyCallback = new AsyncCallback()
	{
		@Override
		public void onSuccess()
		{
		}
		
		@Override
		public void onError(DiameterException ex)
		{
		}
	};
	
	public MessageProcessingTask(DiameterStack stack,DiameterLink link,ConcurrentHashMap genericListeners,AtomicLong lastActivity,AtomicBoolean waitingForDWA,Association association,ByteBuf buffer,DiameterMessage message,AtomicReference> remoteApplicationIds,AtomicReference> remoteAuthApplicationIds,AtomicReference> remoteAcctApplicationIds)
	{
		this.stack = stack;
		this.buffer = buffer;
		this.message = message;		
		this.link = link;		
		this.genericListeners=genericListeners;
		this.lastActivity = lastActivity;
		this.waitingForDWA = waitingForDWA;
		this.association = association;
		this.remoteApplicationIds = remoteApplicationIds;
		this.remoteAuthApplicationIds = remoteAuthApplicationIds;
		this.remoteAcctApplicationIds = remoteAcctApplicationIds;
	}

	@Override
	public void execute()
	{
		try
		{
			lastActivity.set(System.currentTimeMillis());
			stack.messageReceived(message, link.getID());
			
			if(logger.isDebugEnabled())
				logger.debug(String.format("Processing Incoming Message received on link=%s %s", link.getID(), message.getClass().getCanonicalName()));
			
			if(message instanceof CapabilitiesExchangeRequest)
			{
				switch(link.getPeerState())
				{
					case OPEN:
					case IDLE:
						CapabilitiesExchangeRequest request = (CapabilitiesExchangeRequest)message;
						if(message.getOriginHost() == null || !link.getDestinationHost().equals(message.getOriginHost()))
						{
							try
							{
								DiameterException exception = new DiameterException("invalid remote hostname in CER", null, ResultCodes.DIAMETER_UNKNOWN_PEER, null);
								exception.setPartialMessage(request);
								link.sendError(exception);
								return;
							}
							catch(DiameterException ex2)
							{
								logger.warn("An error occured while sending error for incoming message " + ex2.getMessage() + " from " + association, ex2);						
							}
						}
						
						if(message.getOriginRealm() == null || !link.getDestinationRealm().equals(message.getOriginRealm()))
						{
							try
							{
								DiameterException exception = new DiameterException("invalid remote realm in CER", null, ResultCodes.DIAMETER_UNKNOWN_PEER, null);
								exception.setPartialMessage(request);
								link.sendError(exception);
							}
							catch(DiameterException ex2)
							{
								logger.warn("An error occured while sending error for incoming message " + ex2.getMessage() + " from " + association, ex2);						
							}
						}
						
						List matchedAuthApplicationIds = new ArrayList();
						List matchedAcctApplicationIds = new ArrayList();
						List matchedVendorSpecificApplicationIds = new ArrayList();
						
						List realAuthApplicationIds = request.getAuthApplicationIds();
						List realAcctApplicationIds = request.getAcctApplicationIds();
						List realVendorSpecificApplicationIds = request.getVendorSpecificApplicationIds();
						
						if(realAuthApplicationIds!=null)
						{
							for(Long currApplicationId:realAuthApplicationIds)
							{
								for(Long localApplicationId: link.getAuthApplicationIds())
								{
									if(currApplicationId.equals(localApplicationId))
									{
										matchedAuthApplicationIds.add(currApplicationId);
										break;
									}
								}
							}				
						}
						
						if(realAcctApplicationIds!=null)
						{
							for(Long currApplicationId:realAcctApplicationIds)
							{
								for(Long localApplicationId: link.getAcctApplicationIds())
								{
									if(currApplicationId.equals(localApplicationId))
									{
										matchedAcctApplicationIds.add(currApplicationId);
										break;
									}
								}
							}				
						}
						
						if(realVendorSpecificApplicationIds!=null)
						{
							for(VendorSpecificApplicationId currApplicationId:realVendorSpecificApplicationIds)
							{
								for(VendorSpecificApplicationId localApplicationId: link.getVendorSpecificApplicationIds())
								{
									if(DiameterLinkImpl.sameVendorSpecificApplicationId(localApplicationId, currApplicationId))
									{
										matchedVendorSpecificApplicationIds.add(currApplicationId);
										break;
									}
								}
							}				
						}
						
						if(matchedAuthApplicationIds.size() == 0 && matchedAcctApplicationIds.size() == 0 && matchedVendorSpecificApplicationIds.size() == 0)
						{
							try
							{
								DiameterException exception = new DiameterException("No common applications found", null, ResultCodes.DIAMETER_NO_COMMON_APPLICATION, null);
								exception.setPartialMessage(request);
								link.sendError(exception);
							}
							catch(DiameterException ex2)
							{
								logger.warn("An error occured while sending error for incoming message " + ex2.getMessage() + " from " + association, ex2);						
							}
						}
						else
						{
							if(matchedVendorSpecificApplicationIds!=null && matchedVendorSpecificApplicationIds.size()>0)
							{
								for(VendorSpecificApplicationId currVendorSpecificApplicationId:matchedVendorSpecificApplicationIds)
								{
									if(currVendorSpecificApplicationId.getAcctApplicationId()!=null)
									{
										Boolean hasApplicationId = false;
										if(matchedAcctApplicationIds!=null)
										{
											for(Long currAcctApplicationId:matchedAcctApplicationIds)
											{
												if(currAcctApplicationId.equals(currVendorSpecificApplicationId.getAcctApplicationId()))
												{
													hasApplicationId = true;
													break;
												}
											}
										}
										
										if(!hasApplicationId)
											matchedAcctApplicationIds.add(currVendorSpecificApplicationId.getAcctApplicationId());
									}
									
									if(currVendorSpecificApplicationId.getAuthApplicationId()!=null)
									{
										Boolean hasApplicationId = false;
										if(matchedAuthApplicationIds!=null)
										{
											for(Long currAuthApplicationId:matchedAuthApplicationIds)
											{
												if(currAuthApplicationId.equals(currVendorSpecificApplicationId.getAuthApplicationId()))
												{
													hasApplicationId = true;
													break;
												}
											}
										}
										
										if(!hasApplicationId)
											matchedAuthApplicationIds.add(currVendorSpecificApplicationId.getAuthApplicationId());										
									}
								}
							}
							
							remoteAcctApplicationIds.set(matchedAcctApplicationIds);
							remoteAuthApplicationIds.set(matchedAuthApplicationIds);
							remoteApplicationIds.set(matchedVendorSpecificApplicationIds);
							link.setPeerState(PeerStateEnum.OPEN);
							link.sendCEA(request);
						}
						break;
					case CER_SENT:
					case DPR_SENT:
					default:
						try
						{
							DiameterException exception = new DiameterException("Invalid state for peer while handling CER , current state " + link.getPeerState(), null, ResultCodes.DIAMETER_UNABLE_TO_COMPLY, null);
							exception.setPartialMessage(message);
							link.sendError(exception);
						}
						catch(DiameterException ex2)
						{
							logger.warn("An error occured while sending error for incoming message " + ex2.getMessage() + " from " + association, ex2);						
						}
						break;
				
				}
			}
			else if(message instanceof CapabilitiesExchangeAnswer)
			{
				switch(link.getPeerState())
				{
					case CER_SENT:
						CapabilitiesExchangeAnswer answer = (CapabilitiesExchangeAnswer)message;
						if(answer.getIsError()!=null && answer.getIsError())
						{
							link.setPeerState(PeerStateEnum.IDLE);
							link.resetReconnectTimer();
							logger.warn("CER Failed with error " + answer.getResultCode() + " from " + association);
						}
						else
						{
							logger.info("Peer is up for " + association);
							
							remoteAcctApplicationIds.set(answer.getAcctApplicationIds());
							remoteAuthApplicationIds.set(answer.getAuthApplicationIds());
							remoteApplicationIds.set(answer.getVendorSpecificApplicationIds());
							if(answer.getVendorSpecificApplicationIds()!=null && answer.getVendorSpecificApplicationIds().size()>0)
							{
								for(VendorSpecificApplicationId currVendorSpecificApplicationId:answer.getVendorSpecificApplicationIds())
								{
									if(currVendorSpecificApplicationId.getAcctApplicationId()!=null)
									{
										Boolean hasApplicationId = false;
										if(remoteAcctApplicationIds.get()!=null)
										{
											for(Long currAcctApplicationId:remoteAcctApplicationIds.get())
											{
												if(currAcctApplicationId.equals(currVendorSpecificApplicationId.getAcctApplicationId()))
												{
													hasApplicationId = true;
													break;
												}
											}
										}
										else
											remoteAcctApplicationIds.set(new ArrayList());
										
										if(!hasApplicationId)
											remoteAcctApplicationIds.get().add(currVendorSpecificApplicationId.getAcctApplicationId());
									}
									
									if(currVendorSpecificApplicationId.getAuthApplicationId()!=null)
									{
										Boolean hasApplicationId = false;
										if(remoteAuthApplicationIds.get()!=null)
										{
											for(Long currAuthApplicationId:remoteAuthApplicationIds.get())
											{
												if(currAuthApplicationId.equals(currVendorSpecificApplicationId.getAuthApplicationId()))
												{
													hasApplicationId = true;
													break;
												}
											}
										}
										else
											remoteAuthApplicationIds.set(new ArrayList());
										
										if(!hasApplicationId)
											remoteAuthApplicationIds.get().add(currVendorSpecificApplicationId.getAuthApplicationId());										
									}
								}
							}
							
							link.setPeerState(PeerStateEnum.OPEN);
							link.resetInactivityTimer();
						}
						break;
					case OPEN:
					case DPR_SENT:
					case IDLE:
					default:
						logger.warn("Invalid state for peer while handling CEA , current state " + link.getPeerState(), null, ResultCodes.DIAMETER_UNABLE_TO_COMPLY, null);
						break;
				
				}
			}
			else if(message instanceof DeviceWatchdogRequest)
			{
				switch(link.getPeerState())
				{
					case OPEN:
						link.sendDWA((DeviceWatchdogRequest)message,ResultCodes.DIAMETER_SUCCESS);
						break;
					case CER_SENT:
					case DPR_SENT:
					case IDLE:
					default:
						try
						{
							DiameterException exception = new DiameterException("Invalid state for peer while handling DWR , current state " + link.getPeerState(), null, ResultCodes.DIAMETER_UNABLE_TO_COMPLY, null);
							exception.setPartialMessage(message);
							link.sendError(exception);
						}
						catch(DiameterException ex2)
						{
							logger.warn("An error occured while sending error for incoming message " + ex2.getMessage() + " from " + association, ex2);						
						}
						break;
				
				}
			}
			else if(message instanceof DeviceWatchdogAnswer)
			{
				switch(link.getPeerState())
				{
					case OPEN:
						waitingForDWA.set(false);
						break;
					case CER_SENT:
					case DPR_SENT:
					case IDLE:
					default:
						logger.warn("Invalid state for peer while handling DWA , current state " + link.getPeerState());
						break;				
				}
			}
			else if(message instanceof DisconnectPeerRequest)
			{
				switch(link.getPeerState())
				{
					case OPEN:
						link.sendDPA((DisconnectPeerRequest)message, ResultCodes.DIAMETER_SUCCESS);
						link.setPeerState(PeerStateEnum.IDLE);
						link.resetReconnectTimer();
						break;
					case CER_SENT:
					case DPR_SENT:
					case IDLE:
					default:
						try
						{
							DiameterException exception = new DiameterException("Invalid state for peer while handling DPR , current state " + link.getPeerState(), null, ResultCodes.DIAMETER_UNABLE_TO_COMPLY, null);
							exception.setPartialMessage(message);
							link.sendError(exception);
						}
						catch(DiameterException ex2)
						{
							logger.warn("An error occured while sending error for incoming message " + ex2.getMessage() + " from " + association, ex2);						
						}
						break;				
				}
			}
			else if(message instanceof DisconnectPeerAnswer)
			{
				switch(link.getPeerState())
				{
					case DPR_SENT:
						link.disconnectOperationCompleted();
						break;
					case OPEN:
					case CER_SENT:
					case IDLE:
					default:
						logger.warn("Invalid state for peer while handling DPA , current state " + link.getPeerState());
						break;				
				}
			}
			else
			{
				if(stack.isSessionLess()==null || !stack.isSessionLess())
				{
					if(logger.isDebugEnabled())
						logger.debug(String.format("Processing message %s received on link=%s as statefull", message.getClass().getCanonicalName(), link.getID()));
					
					DiameterCommandDefinition commandDef = DiameterParser.getCommandDefinition(message.getClass());
					if(commandDef==null)
						//should not happen , just in case 
						logger.warn("Can find the command definition for " + message.getClass());
					else
					{
						if(logger.isDebugEnabled())
							logger.debug(String.format("checking the package name for message %s received on link=%s", message.getClass().getCanonicalName(), link.getID()));
						
						Package packageName = null;
						try
						{
							if((message instanceof AccountingRequest) || (message instanceof AccountingAnswer))
								packageName = link.getPackage(commandDef.applicationId(), false);
							else 
								packageName = link.getPackage(commandDef.applicationId(), true);
						}
						catch(Exception ex)
						{
							logger.warn("An error occured while getting the package " + ex.getMessage(),ex);
						}
						
						if(packageName == null)
						{
							if(logger.isDebugEnabled())
								logger.debug(String.format("No package has been found message %s received on link=%s , application id %s", message.getClass().getCanonicalName(), link.getID(), String.valueOf(commandDef.applicationId())));
							
							try
							{
								DiameterException exception  = new DiameterException("Application has not been found locally", null, ResultCodes.DIAMETER_APPLICATION_UNSUPPORTED, null);
								exception.setPartialMessage(message);
								link.sendError(exception);
							}
							catch(DiameterException ex2)
							{
								logger.warn("An error occured while sending error for incoming message " + ex2.getMessage() + " from " + association, ex2);						
							}
						}
						
						if(logger.isDebugEnabled())
							logger.debug(String.format("checking for duplicates for message %s received on link=%s , application id %s", message.getClass().getCanonicalName(), link.getID(), String.valueOf(commandDef.applicationId())));
						
						try
						{
							if(message instanceof DiameterRequest)
							{
								DiameterRequest request = (DiameterRequest)message;
								DiameterAnswerData answerData = stack.getRequestsStorage().incomingMessageReceived(request);
								if(answerData!=null)
								{
									if(answerData.getBuffer()!=null)
									{
										link.sendEncodedMessage(answerData.getBuffer(), new AsyncCallback()
										{
											@Override
											public void onSuccess()
											{									
											}
											
											@Override
											public void onError(DiameterException ex)
											{
												logger.warn("An error occured while sending repeated answer," + ex.getMessage(),ex);
											}
										});
									}
									
									if(logger.isDebugEnabled())
										logger.debug(String.format("Duplicate message %s detected on link=%s", message.getClass().getCanonicalName(), link.getID()));
									
									return;																
								}
							}
						}
						catch(Exception ex)
						{
							logger.warn("An error occured while checking for duplicates," + ex.getMessage(), ex);
						}
						
						if(logger.isDebugEnabled())
							logger.debug(String.format("checking for provider for message %s received on link=%s , application id %s", message.getClass().getCanonicalName(), link.getID(), String.valueOf(commandDef.applicationId())));
						
						DiameterProvider provider = stack.getProvider(commandDef.applicationId(), packageName);
						if(provider == null)
						{
							if(logger.isDebugEnabled())
								logger.debug(String.format("No provider has been found for message %s on link=%s", message.getClass().getCanonicalName(), link.getID()));
							
							try
							{
								DiameterException exception = new DiameterException("Application has not been found locally", null, ResultCodes.DIAMETER_APPLICATION_UNSUPPORTED, null);
								exception.setPartialMessage(message);
								link.sendError(exception);
							}
							catch(DiameterException ex2)
							{
								logger.warn("An error occured while sending error for incoming message " + ex2.getMessage() + " from " + association, ex2);						
							}								
						}	
						else
						{
							if(logger.isDebugEnabled())
								logger.debug(String.format("Delivering message %s received on link=%s to provider %s", message.getClass().getCanonicalName(), link.getID() , provider.getClass().getCanonicalName()));
							
							provider.onMessage(message, link.getID(), new AsyncCallback()
							{
								@Override
								public void onSuccess()
								{
									
								}
								
								@Override
								public void onError(DiameterException ex)
								{
									logger.warn("An error occured while delivering incoming message " + ex.getMessage() + " from " + association, ex);
									
									if(ex.getPartialMessage()==null)
										ex.setPartialMessage(message);
									
									try
									{
										link.sendError(ex);
									}
									catch(DiameterException ex2)
									{
										logger.warn("An error occured while sending error for incoming message " + ex2.getMessage() + " from " + association, ex2);						
									}
								}
							});
						}
					}
				}
			}
			
			try
			{
				Iterator> iterator = genericListeners.entrySet().iterator();
				while(iterator.hasNext())
				{
					Entry currEntry = iterator.next();
					currEntry.getValue().onMessage(message, link.getID(), dummyCallback);
				}
			}
			catch(Exception ex)
			{
				logger.warn("An error occured while delivering incoming message " + ex.getMessage() + " from " + association, ex);
			}
		}
		finally 
		{
			//lets try to release the buffer in case its not released yet
			try
			{
				buffer.release();	
			}
			catch(Exception ex)
			{
				
			}
		}
	}

	@Override
	public long getStartTime()
	{
		return startTime;
	}
}	




© 2015 - 2024 Weber Informatics LLC | Privacy Policy