Start of the COPS message refactoring to make all of these classes more semantic...
[packetcable.git] / packetcable-driver / src / main / java / org / umu / cops / stack / COPSReqMsg.java
1 /*\r
2  * Copyright (c) 2003 University of Murcia.  All rights reserved.\r
3  * --------------------------------------------------------------\r
4  * For more information, please see <http://www.umu.euro6ix.org/>.\r
5  */\r
6 \r
7 package org.umu.cops.stack;\r
8 \r
9 import java.io.IOException;\r
10 import java.io.OutputStream;\r
11 import java.net.Socket;\r
12 import java.util.Enumeration;\r
13 import java.util.Hashtable;\r
14 import java.util.Vector;\r
15 \r
16 /**\r
17  * COPS Request Message (RFC 2748 pag. 22)\r
18  *\r
19  *   The PEP establishes a request state client handle for which the\r
20  *   remote PDP may maintain state. The remote PDP then uses this handle\r
21  *   to refer to the exchanged information and decisions communicated over\r
22  *   the TCP connection to a particular PEP for a given client-type.\r
23  *\r
24  *   Once a stateful handle is established for a new request, any\r
25  *   subsequent modifications of the request can be made using the REQ\r
26  *   message specifying the previously installed handle. The PEP is\r
27  *   responsible for notifying the PDP whenever its local state changes so\r
28  *   the PDP's state will be able to accurately mirror the PEP's state.\r
29  *\r
30  *   The format of the Request message is as follows:\r
31  *\r
32  *               <Request Message> ::=  <Common Header>\r
33  *                                      <Client Handle>\r
34  *                                      <Context>\r
35  *                                      [<IN-Int>]\r
36  *                                      [<OUT-Int>]\r
37  *                                      [<ClientSI(s)>]\r
38  *                                      [<LPDPDecision(s)>]\r
39  *                                      [<Integrity>]\r
40  *\r
41  *               <ClientSI(s)> ::= <ClientSI> | <ClientSI(s)> <ClientSI>\r
42  *\r
43  *               <LPDPDecision(s)> ::= <LPDPDecision> |\r
44  *                                     <LPDPDecision(s)> <LPDPDecision>\r
45  *\r
46  *               <LPDPDecision> ::= [<Context>]\r
47  *                                  <LPDPDecision: Flags>\r
48  *                                  [<LPDPDecision: Stateless Data>]\r
49  *                                  [<LPDPDecision: Replacement Data>]\r
50  *                                  [<LPDPDecision: ClientSI Data>]\r
51  *                                  [<LPDPDecision: Named Data>]\r
52  *\r
53  *   The context object is used to determine the context within which all\r
54  *   the other objects are to be interpreted. It also is used to determine\r
55  *   the kind of decision to be returned from the policy server. This\r
56  *   decision might be related to admission control, resource allocation,\r
57  *   object forwarding and substitution, or configuration.\r
58  *\r
59  *   The interface objects are used to determine the corresponding\r
60  *   interface on which a signaling protocol message was received or is\r
61  *   about to be sent. They are typically used if the client is\r
62  *   participating along the path of a signaling protocol or if the client\r
63  *   is requesting configuration data for a particular interface.\r
64  *\r
65  *   ClientSI, the client specific information object, holds the client-\r
66  *   type specific data for which a policy decision needs to be made. In\r
67  *   the case of configuration, the Named ClientSI may include named\r
68  *   information about the module, interface, or functionality to be\r
69  *   configured. The ordering of multiple ClientSIs is not important.\r
70  *\r
71  *   Finally, LPDPDecision object holds information regarding the local\r
72  *   decision made by the LPDP.\r
73  *\r
74  *   Malformed Request messages MUST result in the PDP specifying a\r
75  *   Decision message with the appropriate error code.\r
76  *\r
77  * @version COPSReqMsg.java, v 1.00 2003\r
78  *\r
79  */\r
80 public class COPSReqMsg extends COPSMsg {\r
81 \r
82     /* COPSHeader coming from base class */\r
83     private COPSHandle _clientHandle;\r
84     private COPSContext _context;\r
85     private COPSInterface _inInterface;\r
86     private COPSInterface _outInterface;\r
87     private Vector _clientSIs;\r
88     private Hashtable _decisions;\r
89     private COPSIntegrity _integrity;\r
90     private COPSContext _lpdpContext;\r
91 \r
92     public COPSReqMsg() {\r
93         _clientHandle = null;\r
94         _context = null;\r
95         _inInterface = null;\r
96         _outInterface = null;\r
97         _clientSIs = new Vector(20);\r
98         _decisions = new Hashtable();\r
99         _integrity = null;\r
100         _lpdpContext = null;\r
101     }\r
102 \r
103     /**\r
104           Parse data and create COPSReqMsg object\r
105      */\r
106     protected COPSReqMsg(byte[] data) throws COPSException {\r
107         parse(data);\r
108     }\r
109 \r
110     /**\r
111      * Checks the sanity of COPS message and throw an\r
112      * COPSBadDataException when data is bad.\r
113      */\r
114     public void checkSanity() throws COPSException {\r
115         if ((_hdr == null) || (_clientHandle == null) || (_context == null)) {\r
116             throw new COPSException("Bad message format");\r
117         }\r
118     }\r
119 \r
120     /**\r
121      * Add an IN or OUT interface object\r
122      *\r
123      * @param    inter               a  COPSInterface\r
124      *\r
125      * @throws   COPSException\r
126      *\r
127      */\r
128     public void add (COPSInterface inter) throws COPSException {\r
129         if (!(inter.isInInterface() || inter.isOutInterface()))\r
130             throw new COPSException ("No Interface");\r
131 \r
132         //Message integrity object should be the very last one\r
133         //If it is already added\r
134         if (_integrity != null)\r
135             throw new COPSException ("Integrity should be the last one");\r
136 \r
137         if (inter.isInInterface()) {\r
138             if (_inInterface != null)\r
139                 throw new COPSException ("Object inInterface exits");\r
140 \r
141             if (inter.isIpv4Address()) {\r
142                 COPSIpv4InInterface inInter = (COPSIpv4InInterface) inter;\r
143                 _inInterface = inInter;\r
144             } else {\r
145                 COPSIpv6InInterface inInter = (COPSIpv6InInterface) inter;\r
146                 _inInterface = inInter;\r
147             }\r
148         } else {\r
149             if (_outInterface != null)\r
150                 throw new COPSException ("Object outInterface exits");\r
151 \r
152             if (inter.isIpv4Address()) {\r
153                 COPSIpv4OutInterface outInter = (COPSIpv4OutInterface) inter;\r
154                 _outInterface = outInter;\r
155             } else {\r
156                 COPSIpv6OutInterface outInter = (COPSIpv6OutInterface) inter;\r
157                 _outInterface = outInter;\r
158             }\r
159         }\r
160         setMsgLength();\r
161     }\r
162 \r
163     /**\r
164      * Add header to the message\r
165      *\r
166      * @param    hdr                 a  COPSHeader\r
167      *\r
168      * @throws   COPSException\r
169      *\r
170      */\r
171     public void add (COPSHeader hdr) throws COPSException {\r
172         if (hdr == null)\r
173             throw new COPSException ("Null Header");\r
174         if (hdr.getOpCode() != COPSHeader.COPS_OP_REQ)\r
175             throw new COPSException ("Error Header (no COPS_OP_REQ)");\r
176         _hdr = hdr;\r
177         setMsgLength();\r
178     }\r
179 \r
180     /**\r
181      * Add Context object to the message\r
182      *\r
183      * @param    context             a  COPSContext\r
184      *\r
185      * @throws   COPSException\r
186      *\r
187      */\r
188     public void add (COPSContext context) throws COPSException {\r
189         if (context == null)\r
190             throw new COPSException ("Null Context");\r
191         _context = context;\r
192         setMsgLength();\r
193     }\r
194 \r
195     /**\r
196      * Add client handle to the message\r
197      *\r
198      * @param    handle              a  COPSHandle\r
199      *\r
200      * @throws   COPSException\r
201      *\r
202      */\r
203     public void add (COPSHandle handle) throws COPSException {\r
204         if (handle == null)\r
205             throw new COPSException ("Null Handle");\r
206         _clientHandle = handle;\r
207         setMsgLength();\r
208     }\r
209 \r
210     /**\r
211      * Add one or more clientSI objects\r
212      *\r
213      * @param    clientSI            a  COPSClientSI\r
214      *\r
215      * @throws   COPSException\r
216      *\r
217      */\r
218     public void add (COPSClientSI clientSI) throws COPSException {\r
219         if (clientSI == null)\r
220             throw new COPSException ("Null ClientSI");\r
221         _clientSIs.add(clientSI);\r
222         setMsgLength();\r
223     }\r
224 \r
225     /**\r
226      * Add one or more local decision object for a given decision context\r
227      * the context is optional, if null all decision object are tided to\r
228      * message context\r
229      *\r
230      * @param    decision            a  COPSLPDPDecision\r
231      * @param    context             a  COPSContext\r
232      *\r
233      * @throws   COPSException\r
234      *\r
235      */\r
236     public void addLocalDecision(COPSLPDPDecision decision, COPSContext context) throws COPSException {\r
237         if (!decision.isLocalDecision())\r
238             throw new COPSException ("Local Decision");\r
239 \r
240         Vector v = (Vector) _decisions.get(context);\r
241         if (decision.isFlagSet()) {\r
242             if (v.size() != 0) {\r
243                 //Only one set of decision flags is allowed\r
244                 //for each context\r
245                 throw new COPSException ("Bad Message format, only one set of decision flags is allowed.");\r
246             }\r
247         } else {\r
248             if (v.size() == 0) {\r
249                 //The flags decision must precede any other\r
250                 //decision message, since the decision is not\r
251                 //flags throw exception\r
252                 throw new COPSException ("Bad Message format, flags decision must precede any other decision object.");\r
253             }\r
254         }\r
255         v.add(decision);\r
256         _decisions.put(context,v);\r
257 \r
258         setMsgLength();\r
259     }\r
260 \r
261     /**\r
262      * Add integrity object\r
263      *\r
264      * @param    integrity           a  COPSIntegrity\r
265      *\r
266      * @throws   COPSException\r
267      *\r
268      */\r
269     public void add (COPSIntegrity integrity) throws COPSException {\r
270         if (integrity == null)\r
271             throw new COPSException ("Null Integrity");\r
272         if (!integrity.isMessageIntegrity())\r
273             throw new COPSException ("Error Integrity");\r
274         _integrity = integrity;\r
275         setMsgLength();\r
276     }\r
277 \r
278     /**\r
279      * Writes data to given socket\r
280      *\r
281      * @param    id                  a  Socket\r
282      *\r
283      * @throws   IOException\r
284      *\r
285      */\r
286     public void writeData(Socket id) throws IOException {\r
287         // checkSanity();\r
288         if (_hdr != null) _hdr.writeData(id);\r
289         if (_clientHandle != null) _clientHandle.writeData(id);\r
290         if (_context != null) _context.writeData(id);\r
291 \r
292         for (Enumeration e = _clientSIs.elements() ; e.hasMoreElements() ;) {\r
293             COPSClientSI clientSI = (COPSClientSI) e.nextElement();\r
294             clientSI.writeData(id);\r
295         }\r
296 \r
297         //Display any local decisions\r
298         for (Enumeration e = _decisions.keys() ; e.hasMoreElements() ;) {\r
299 \r
300             COPSContext context = (COPSContext) e.nextElement();\r
301             Vector v = (Vector) _decisions.get(context);\r
302             context.writeData(id);\r
303 \r
304             for (Enumeration ee = v.elements() ; e.hasMoreElements() ;) {\r
305                 COPSLPDPDecision decision = (COPSLPDPDecision) ee.nextElement();\r
306                 decision.writeData(id);\r
307             }\r
308         }\r
309 \r
310         if (_integrity != null) _integrity.writeData(id);\r
311 \r
312     }\r
313 \r
314     /**\r
315      * Return Header\r
316      *\r
317      * @return   a COPSHeader\r
318      *\r
319      */\r
320     public COPSHeader getHeader() {\r
321         return _hdr;\r
322     }\r
323 \r
324     /**\r
325      * Return client Handle\r
326      *\r
327      * @return   a COPSHandle\r
328      *\r
329      */\r
330     public COPSHandle getClientHandle() {\r
331         return _clientHandle;\r
332     }\r
333 \r
334     /**\r
335      * Return Context\r
336      *\r
337      * @return   a COPSContext\r
338      *\r
339      */\r
340     public COPSContext getContext() {\r
341         return _context;\r
342     }\r
343 \r
344     /**\r
345      * Returns true if it has In Interface\r
346      *\r
347      * @return   a boolean\r
348      *\r
349      */\r
350     public boolean hasInInterface() {\r
351         return (_inInterface == null);\r
352     }\r
353 \r
354     /**\r
355      * Should check hasInInterface() before calling\r
356      *\r
357      * @return   a COPSInterface\r
358      *\r
359      */\r
360     public COPSInterface getInInterface() {\r
361         return _inInterface;\r
362     }\r
363 \r
364     /**\r
365      * Returns true if it has Out interface\r
366      *\r
367      * @return   a boolean\r
368      *\r
369      */\r
370     public boolean hasOutInterface() {\r
371         return (_outInterface == null);\r
372     }\r
373 \r
374     /**\r
375      * Should check hasOutInterface() before calling\r
376      *\r
377      * @return   a COPSInterface\r
378      *\r
379      */\r
380     public COPSInterface getOutInterface() {\r
381         return _outInterface;\r
382     }\r
383 \r
384     /**\r
385      * Returns a vector if ClientSI objects\r
386      *\r
387      * @return   a Vector\r
388      *\r
389      */\r
390     public Vector getClientSI() {\r
391         return _clientSIs;\r
392     }\r
393 \r
394     /**\r
395      * Returns a HashTable of any local decisions\r
396      *\r
397      * @return   a Hashtable\r
398      *\r
399      */\r
400     public Hashtable getLpdpDecisions() {\r
401         return _decisions;\r
402     }\r
403 \r
404     /**\r
405      * Returns true if it has Integrity object\r
406      *\r
407      * @return   a boolean\r
408      *\r
409      */\r
410     public boolean hasIntegrity() {\r
411         return (_integrity == null);\r
412     }\r
413 \r
414     /**\r
415      * Get Integrity. Should check hasIntegrity() becfore calling\r
416      *\r
417      * @return   a COPSIntegrity\r
418      *\r
419      */\r
420     public COPSIntegrity getIntegrity() {\r
421         return _integrity;\r
422     }\r
423 \r
424     /**\r
425      * Parses the data and fills COPSReqMsg with its constituents\r
426      *\r
427      * @param    data                a  byte[]\r
428      *\r
429      * @throws   COPSException\r
430      *\r
431      */\r
432     protected void parse(byte[] data) throws COPSException {\r
433         super.parseHeader(data);\r
434 \r
435         while (_dataStart < _dataLength) {\r
436             byte[] buf = new byte[data.length - _dataStart];\r
437             System.arraycopy(data,_dataStart,buf,0,data.length - _dataStart);\r
438 \r
439             COPSObjHeader objHdr = COPSObjHeader.parse(buf);\r
440             switch (objHdr.getCNum()) {\r
441                 case HANDLE:\r
442                     _clientHandle = new COPSHandle(buf);\r
443                     _dataStart += _clientHandle.getDataLength();\r
444                     break;\r
445                 case CONTEXT:\r
446                     if (_context == null) {\r
447                         //Message context\r
448                         _context = new COPSContext(buf);\r
449                         _dataStart += _context.getDataLength();\r
450                     } else {\r
451                         //lpdp context\r
452                         _lpdpContext = new COPSContext(buf);\r
453                         _dataStart += _lpdpContext.getDataLength();\r
454                     }\r
455                     break;\r
456                 case ININTF:\r
457                     if (objHdr.getCType().ordinal() == 1) {\r
458                         _inInterface = new COPSIpv4InInterface(buf);\r
459                     } else {\r
460                         _inInterface = new COPSIpv6InInterface(buf);\r
461                     }\r
462                     _dataStart += _inInterface.getDataLength();\r
463                     break;\r
464                 case OUTINTF:\r
465                     if (objHdr.getCType().ordinal() == 1) {\r
466                         _outInterface = new COPSIpv4OutInterface(buf);\r
467                     } else {\r
468                         _outInterface = new COPSIpv6OutInterface(buf);\r
469                     }\r
470                     _dataStart += _outInterface.getDataLength();\r
471                 break;\r
472                 case LPDP_DEC:\r
473                     COPSLPDPDecision lpdp = new COPSLPDPDecision(buf);\r
474                     _dataStart += lpdp.getDataLength();\r
475                     addLocalDecision(lpdp, _lpdpContext);\r
476                     break;\r
477                 case CSI:\r
478                     COPSClientSI csi = new COPSClientSI (buf);\r
479                     _dataStart += csi.getDataLength();\r
480                     _clientSIs.add(csi);\r
481                     break;\r
482                 case MSG_INTEGRITY:\r
483                     _integrity = new COPSIntegrity(buf);\r
484                     _dataStart += _integrity.getDataLength();\r
485                     break;\r
486                 default:\r
487                     throw new COPSException("Bad Message format, unknown object type");\r
488             }\r
489         }\r
490         checkSanity();\r
491 \r
492     }\r
493 \r
494     /**\r
495      * Parses the data and fills that follows the header hdr and fills COPSReqMsg\r
496      *\r
497      * @param    hdr                 a  COPSHeader\r
498      * @param    data                a  byte[]\r
499      *\r
500      * @throws   COPSException\r
501      *\r
502      */\r
503     protected void parse(COPSHeader hdr, byte[] data) throws COPSException {\r
504         _hdr = hdr;\r
505         parse(data);\r
506         setMsgLength();\r
507     }\r
508 \r
509     /**\r
510      * Set the message length, base on the set of objects it contains\r
511      *\r
512      * @throws   COPSException\r
513      *\r
514      */\r
515     protected void setMsgLength() throws COPSException {\r
516         short len = 0;\r
517 \r
518         if (_clientHandle != null)\r
519             len += _clientHandle.getDataLength();\r
520 \r
521         if (_context != null)\r
522             len += _context.getDataLength();\r
523 \r
524         for (Enumeration e = _clientSIs.elements() ; e.hasMoreElements() ;) {\r
525             COPSClientSI clientSI = (COPSClientSI) e.nextElement();\r
526             len += clientSI.getDataLength();\r
527         }\r
528 \r
529         //Display any local decisions\r
530         for (Enumeration e = _decisions.keys() ; e.hasMoreElements() ;) {\r
531 \r
532             COPSContext context = (COPSContext) e.nextElement();\r
533             Vector v = (Vector) _decisions.get(context);\r
534             len += context.getDataLength();\r
535 \r
536             for (Enumeration ee = v.elements() ; e.hasMoreElements() ;) {\r
537                 COPSLPDPDecision decision = (COPSLPDPDecision) ee.nextElement();\r
538                 len += decision.getDataLength();\r
539             }\r
540         }\r
541 \r
542         if (_integrity != null) {\r
543             len += _integrity.getDataLength();\r
544         }\r
545 \r
546         _hdr.setMsgLength((int) len);\r
547 \r
548     }\r
549 \r
550     /**\r
551      * Write an object textual description in the output stream\r
552      *\r
553      * @param    os                  an OutputStream\r
554      *\r
555      * @throws   IOException\r
556      *\r
557      */\r
558     public void dump(OutputStream os) throws IOException {\r
559         _hdr.dump(os);\r
560 \r
561         if (_clientHandle != null)\r
562             _clientHandle.dump(os);\r
563 \r
564         if (_context != null)\r
565             _context.dump(os);\r
566 \r
567         for (Enumeration e = _clientSIs.elements() ; e.hasMoreElements() ;) {\r
568             COPSClientSI clientSI = (COPSClientSI) e.nextElement();\r
569             clientSI.dump(os);\r
570         }\r
571 \r
572         //Display any local decisions\r
573         for (Enumeration e = _decisions.keys() ; e.hasMoreElements() ;) {\r
574 \r
575             COPSContext context = (COPSContext) e.nextElement();\r
576             Vector v = (Vector) _decisions.get(context);\r
577             context.dump(os);\r
578 \r
579             for (Enumeration ee = v.elements() ; e.hasMoreElements() ;) {\r
580                 COPSLPDPDecision decision = (COPSLPDPDecision) ee.nextElement();\r
581                 decision.dump(os);\r
582             }\r
583         }\r
584 \r
585         if (_integrity != null) {\r
586             _integrity.dump(os);\r
587         }\r
588     }\r
589 }\r
590 \r