/*
* Copyright (c) 2003 University of Murcia. All rights reserved.
* --------------------------------------------------------------
* For more information, please see .
*/
package org.umu.cops.stack;
import org.umu.cops.stack.COPSHeader.Flag;
import org.umu.cops.stack.COPSHeader.OPCode;
import org.umu.cops.stack.COPSObjHeader.CType;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.*;
/**
* COPS Request Message (RFC 2748 page. 22)
*
* The PEP establishes a request state client handle for which the
* remote PDP may maintain state. The remote PDP then uses this handle
* to refer to the exchanged information and decisions communicated over
* the TCP connection to a particular PEP for a given client-type.
*
* Once a stateful handle is established for a new request, any
* subsequent modifications of the request can be made using the REQ
* message specifying the previously installed handle. The PEP is
* responsible for notifying the PDP whenever its local state changes so
* the PDP's state will be able to accurately mirror the PEP's state.
*
* The format of the Request message is as follows:
*
* ::=
*
*
* []
* []
* []
* []
* []
*
* ::= |
*
* ::= |
*
*
* ::= []
*
* []
* []
* []
* []
*
* The context object is used to determine the context within which all
* the other objects are to be interpreted. It also is used to determine
* the kind of decision to be returned from the policy server. This
* decision might be related to admission control, resource allocation,
* object forwarding and substitution, or configuration.
*
* The interface objects are used to determine the corresponding
* interface on which a signaling protocol message was received or is
* about to be sent. They are typically used if the client is
* participating along the path of a signaling protocol or if the client
* is requesting configuration data for a particular interface.
*
* ClientSI, the client specific information object, holds the client-
* type specific data for which a policy decision needs to be made. In
* the case of configuration, the Named ClientSI may include named
* information about the module, interface, or functionality to be
* configured. The ordering of multiple ClientSIs is not important.
*
* Finally, LPDPDecision object holds information regarding the local
* decision made by the LPDP.
*
* Malformed Request messages MUST result in the PDP specifying a
* Decision message with the appropriate error code.
*
* @version COPSReqMsg.java, v 1.00 2003
*
*/
public class COPSReqMsg extends COPSMsg {
// Required Attributes
private final COPSHandle _clientHandle;
private final COPSContext _context;
// Optional Attributes
private final COPSInterface _inInterface;
private final COPSInterface _outInterface;
private final COPSIntegrity _integrity;
// Collection Attributes (can be empty)
private final Set _clientSIs;
private final Map> _decisions;
/**
* Constructor (generally used for sending messages) which probably should not be used as the PCMM version and
* Flag values on the header are being hardcoded to 1 and UNSOLICITED respectively. Use the next one below instead
* @param clientType - the type of client that created the message (required)
* @param handle - the COPS Handle (required)
* @param context - the COPS Context (required)
* @param integrity - the COPS Integrity (optional)
* @param inInterface - the In Interface (optional)
* @param outInterface - the Out interface (optional)
* @param clientSIs - the Client SIs (optional)
* @param decisions - the Decisions by context (optional)
* @throws java.lang.IllegalArgumentException
*/
@Deprecated
public COPSReqMsg(final short clientType, final COPSHandle handle, final COPSContext context,
final COPSIntegrity integrity, final COPSInterface inInterface, final COPSInterface outInterface,
final Set clientSIs, final Map> decisions) {
this(1, Flag.UNSOLICITED, clientType, handle, context, integrity, inInterface, outInterface,
clientSIs, decisions);
}
/**
* Recommended constructor generally for use by a client sending messages.
* @param version - the supported PCMM Version (required)
* @param flag - the flag... (required)
* @param clientType - the type of client that created the message (required)
* @param handle - the COPS Handle (required)
* @param context - the COPS Context (required)
* @param integrity - the COPS Integrity (optional)
* @param inInterface - the In Interface (optional)
* @param outInterface - the Out interface (optional)
* @param clientSIs - the Client SIs (optional)
* @param decisions - the Decisions by context (optional)
* @throws java.lang.IllegalArgumentException
*/
public COPSReqMsg(final int version, final Flag flag, final short clientType, final COPSHandle handle,
final COPSContext context, final COPSIntegrity integrity, final COPSInterface inInterface,
final COPSInterface outInterface, final Set clientSIs,
final Map> decisions) {
this(new COPSHeader(version, flag, OPCode.REQ, clientType), handle, context, integrity, inInterface,
outInterface, clientSIs, decisions);
}
/**
* Constructor generally used when parsing the bytes of an inbound COPS message but can also be used when the
* COPSHeader information is known.
* @param hdr - COPS Header (required)
* @param handle - the COPS Handle (required)
* @param context - the COPS Context (required)
* @param integrity - the COPS Integrity (optional)
* @param inInterface - the In Interface (optional)
* @param outInterface - the Out interface (optional)
* @param clientSIs - the Client SIs (optional)
* @param decisions - the Decisions by context (optional)
* @throws java.lang.IllegalArgumentException
*/
protected COPSReqMsg(final COPSHeader hdr, final COPSHandle handle, final COPSContext context,
final COPSIntegrity integrity, final COPSInterface inInterface, final COPSInterface outInterface,
final Set clientSIs, final Map> decisions) {
super(hdr);
if (!hdr.getOpCode().equals(OPCode.REQ))
throw new IllegalArgumentException("OPCode must be of type - " + OPCode.REQ);
if (handle == null) throw new IllegalArgumentException("COPSHandle must not be null");
if (context == null) throw new IllegalArgumentException("COPSContext must not be null");
_clientHandle = handle;
_context = context;
_integrity = integrity;
_inInterface = inInterface;
_outInterface = outInterface;
if (clientSIs == null) _clientSIs = Collections.unmodifiableSet(new HashSet());
else _clientSIs = Collections.unmodifiableSet(clientSIs);
if (decisions == null) _decisions = Collections.unmodifiableMap(new HashMap>());
else _decisions = Collections.unmodifiableMap(decisions);
}
// Getters of optional members - all can return null
public COPSIntegrity getIntegrity() { return _integrity; }
public COPSInterface getInInterface() { return _inInterface; }
public COPSInterface getOutInterface() { return _outInterface; }
// public COPSContext getLpdpContext() { return _lpdpContext; }
@Override
protected void writeBody(final Socket socket) throws IOException {
_clientHandle.writeData(socket);
_context.writeData(socket);
if (_inInterface != null) _inInterface.writeData(socket);
if (_outInterface != null) _outInterface.writeData(socket);
for (final COPSClientSI clientSI : _clientSIs) {
clientSI.writeData(socket);
}
//Display any local decisions
for (final Map.Entry> entry : _decisions.entrySet()) {
entry.getKey().writeData(socket);
for (final COPSDecision decision : entry.getValue()) {
decision.writeData(socket);
}
}
if (_integrity != null) _integrity.writeData(socket);
}
@Override
protected int getDataLength() {
int out = _context.getDataLength() + _context.getHeader().getHdrLength();
out += _clientHandle.getDataLength() + _clientHandle.getHeader().getHdrLength();
if (_inInterface != null) out += _inInterface.getDataLength() + _inInterface.getHeader().getHdrLength();
if (_outInterface != null) out += _outInterface.getDataLength() + _outInterface.getHeader().getHdrLength();
for (final COPSClientSI clientSI : _clientSIs) {
out += clientSI.getDataLength() + clientSI.getHeader().getHdrLength();
}
for (final Map.Entry> entry : _decisions.entrySet()) {
out += entry.getKey().getDataLength() + entry.getKey().getHeader().getHdrLength();
for (final COPSDecision decision : entry.getValue()) {
out += decision.getDataLength() + decision.getHeader().getHdrLength();
}
}
if (_integrity != null) out += _integrity.getDataLength() + _integrity.getHeader().getHdrLength();
return out;
}
/**
* Return client Handle
* @return a COPSHandle
*/
public COPSHandle getClientHandle() {
return _clientHandle;
}
/**
* Return Context
* @return a COPSContext
*/
public COPSContext getContext() {
return _context;
}
/**
* Returns a Set of ClientSI objects
* @return not null but can be empty
*/
public Set getClientSI() {
return _clientSIs;
}
/**
* Returns a Map of COPSDecision objects
* @return not null but can be empty
*/
public Map> getDecisions() {
return _decisions;
}
@Override
protected void dumpBody(final OutputStream os) throws IOException {
if (_clientHandle != null)
_clientHandle.dump(os);
if (_context != null)
_context.dump(os);
for (final COPSClientSI clientSI : _clientSIs) {
clientSI.dump(os);
}
//Display any local decisions
for (final Map.Entry> entry : _decisions.entrySet()) {
entry.getKey().dump(os);
for (final COPSDecision decision : entry.getValue()) {
decision.dump(os);
}
}
if (_integrity != null) {
_integrity.dump(os);
}
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof COPSReqMsg)) {
return false;
}
if (!super.equals(o)) {
return false;
}
final COPSReqMsg that = (COPSReqMsg) o;
if (this._clientSIs.size() != that._clientSIs.size()) return false;
for (final COPSClientSI thisClientSI : this._clientSIs) {
boolean found = false;
for (final COPSClientSI thatClientSI: that._clientSIs) {
if (thisClientSI.equals(thatClientSI)) {
found = true;
break;
}
}
if (! found) return false;
}
for (final Map.Entry> entry : this._decisions.entrySet()) {
final Set thatDecisions = that._decisions.get(entry.getKey());
if (thatDecisions == null) return false;
for (final COPSLPDPDecision thisDecision : entry.getValue()) {
boolean found = false;
for (final COPSLPDPDecision thatDecision: thatDecisions) {
if (thisDecision.equals(thatDecision)) {
found = true;
break;
}
}
if (! found) return false;
}
}
if (!this._clientHandle.equals(that._clientHandle)) return false;
if (!this._context.equals(that._context)) return false;
if (this._integrity == null && that._integrity != null) return false;
if (this._integrity != null && that._integrity == null) return false;
if (this._integrity != null && that._integrity != null)
if (!this._integrity.equals(that._integrity)) return false;
if (this._inInterface == null && that._inInterface != null) return false;
if (this._inInterface != null && that._inInterface == null) return false;
if (this._inInterface != null && that._inInterface != null)
if (!this._inInterface.equals(that._inInterface)) return false;
if (this._outInterface == null && that._outInterface != null) return false;
if (this._outInterface != null && that._outInterface == null) return false;
if (this._outInterface != null && that._outInterface != null)
if (!this._outInterface.equals(that._outInterface)) return false;
return true;
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + _clientHandle.hashCode();
result = 31 * result + _context.hashCode();
result = 31 * result + (_inInterface != null ? _inInterface.hashCode() : 0);
result = 31 * result + (_outInterface != null ? _outInterface.hashCode() : 0);
result = 31 * result + _clientSIs.hashCode();
result = 31 * result + _decisions.hashCode();
result = 31 * result + (_integrity != null ? _integrity.hashCode() : 0);
return result;
}
/**
* Responsible for parsing a byte array to create a COPSReqMsg object
* @param hdrData - the object's header data
* @param data - the byte array to parse
* @return - the message object
* @throws COPSException
*/
public static COPSReqMsg parse(final COPSHeaderData hdrData, final byte[] data) throws COPSException {
// Variables for constructor
COPSHandle clientHandle = null;
COPSContext context = null;
COPSContext lpdpContext = null;
COPSIntegrity integrity = null;
COPSInterface inInterface = null;
COPSInterface outInterface = null;
Set clientSIs = new HashSet<>();
Map> localDecisions = new HashMap<>();
int dataStart = 0;
while (dataStart < data.length) {
final byte[] buf = new byte[data.length - dataStart];
System.arraycopy(data, dataStart, buf, 0, data.length - dataStart);
final COPSObjHeaderData objHdrData = COPSObjectParser.parseObjHeader(buf);
switch (objHdrData.header.getCNum()) {
case HANDLE:
clientHandle = COPSHandle.parse(objHdrData, buf);
break;
case CONTEXT:
if (context == null) context = COPSContext.parse(objHdrData, buf);
else lpdpContext = COPSContext.parse(objHdrData, buf);
break;
case ININTF:
if (objHdrData.header.getCType().equals(CType.DEF)) {
inInterface = COPSObjectParser.parseIpv4Interface(objHdrData, buf, true);
} else inInterface = COPSObjectParser.parseIpv6Interface(objHdrData, buf, true);
break;
case OUTINTF:
if (objHdrData.header.getCType().equals(CType.DEF)) {
outInterface = COPSObjectParser.parseIpv4Interface(objHdrData, buf, false);
} else outInterface = COPSObjectParser.parseIpv6Interface(objHdrData, buf, false);
break;
case LPDP_DEC:
if (localDecisions.get(lpdpContext) != null)
localDecisions.get(lpdpContext).add(COPSLPDPDecision.parse(objHdrData, buf));
else {
final Set decisions = new HashSet<>();
decisions.add(COPSLPDPDecision.parse(objHdrData, buf));
localDecisions.put(lpdpContext, decisions);
}
break;
case CSI:
clientSIs.add(COPSClientSI.parse(objHdrData, buf));
break;
case MSG_INTEGRITY:
integrity = COPSIntegrity.parse(objHdrData, buf);
break;
default:
throw new COPSException("Bad Message format, unknown object type");
}
dataStart += objHdrData.msgByteCount;
}
return new COPSReqMsg(hdrData.header, clientHandle, context, integrity, inInterface, outInterface,
clientSIs, localDecisions);
}
}