more progress -- still not compiling
[captive-validator.git] / src / com / versign / tat / dnssec / SMessage.java
1 /*
2  * $Id$
3  * 
4  * Copyright (c) 2005 VeriSign. All rights reserved.
5  * 
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  * 
9  * 1. Redistributions of source code must retain the above copyright notice,
10  * this list of conditions and the following disclaimer. 2. Redistributions in
11  * binary form must reproduce the above copyright notice, this list of
12  * conditions and the following disclaimer in the documentation and/or other
13  * materials provided with the distribution. 3. The name of the author may not
14  * be used to endorse or promote products derived from this software without
15  * specific prior written permission.
16  * 
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
20  * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
22  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *  
28  */
29
30 package com.versign.tat.dnssec;
31
32 import java.util.*;
33
34 import org.xbill.DNS.*;
35
36 /**
37  * This class represents a DNS message with resolver/validator state.
38  */
39 public class SMessage
40 {
41   private Header          mHeader;
42
43   private Record          mQuestion;
44   private OPTRecord       mOPTRecord;
45   private List[]          mSection;
46   private SecurityStatus  mSecurityStatus;
47
48   private static SRRset[] empty_srrset_array = new SRRset[0];
49
50   public SMessage(Header h)
51   {
52     mSection = new List[3];
53     mHeader = h;
54     mSecurityStatus = new SecurityStatus();
55   }
56
57   public SMessage(int id)
58   {
59     this(new Header(id));
60   }
61
62   public SMessage()
63   {
64     this(new Header(0));
65   }
66
67   public SMessage(Message m)
68   {
69     this(m.getHeader());
70     mQuestion = m.getQuestion();
71     mOPTRecord = m.getOPT();
72
73     for (int i = Section.ANSWER; i <= Section.ADDITIONAL; i++)
74     {
75       RRset[] rrsets = m.getSectionRRsets(i);
76
77       for (int j = 0; j < rrsets.length; j++)
78       {
79         addRRset(rrsets[j], i);
80       }
81     }
82   }
83
84   public Header getHeader()
85   {
86     return mHeader;
87   }
88
89   public void setHeader(Header h)
90   {
91     mHeader = h;
92   }
93
94   public void setQuestion(Record r)
95   {
96     mQuestion = r;
97   }
98
99   public Record getQuestion()
100   {
101     return mQuestion;
102   }
103
104   public Name getQName() {
105       return getQuestion().getName();
106   }
107   
108   public int getQType() {
109       return getQuestion().getType();
110   }
111   
112   public int getQClass() {
113       return getQuestion().getDClass();
114   }
115   
116   public void setOPT(OPTRecord r)
117   {
118     mOPTRecord = r;
119   }
120
121   public OPTRecord getOPT()
122   {
123     return mOPTRecord;
124   }
125
126   public List getSectionList(int section)
127   {
128     if (section <= Section.QUESTION || section > Section.ADDITIONAL)
129       throw new IllegalArgumentException("Invalid section.");
130
131     if (mSection[section - 1] == null)
132     {
133       mSection[section - 1] = new LinkedList();
134     }
135
136     return mSection[section - 1];
137   }
138
139   public void addRRset(SRRset srrset, int section)
140   {
141     if (section <= Section.QUESTION || section > Section.ADDITIONAL)
142       throw new IllegalArgumentException("Invalid section");
143
144     if (srrset.getType() == Type.OPT)
145     {
146       mOPTRecord = (OPTRecord) srrset.first();
147       return;
148     }
149
150     List sectionList = getSectionList(section);
151     sectionList.add(srrset);
152   }
153
154   public void addRRset(RRset rrset, int section)
155   {
156     if (rrset instanceof SRRset)
157     {
158       addRRset((SRRset) rrset, section);
159       return;
160     }
161
162     SRRset srrset = new SRRset(rrset);
163     addRRset(srrset, section);
164   }
165
166   public void prependRRsets(List rrsets, int section)
167   {
168     if (section <= Section.QUESTION || section > Section.ADDITIONAL)
169       throw new IllegalArgumentException("Invalid section");
170
171     List sectionList = getSectionList(section);
172     sectionList.addAll(0, rrsets);
173   }
174
175   public SRRset[] getSectionRRsets(int section)
176   {
177     List slist = getSectionList(section);
178
179     return (SRRset[]) slist.toArray(empty_srrset_array);
180   }
181
182   public SRRset[] getSectionRRsets(int section, int qtype)
183   {
184     List slist = getSectionList(section);
185
186     if (slist.size() == 0) return new SRRset[0];
187
188     ArrayList result = new ArrayList(slist.size());
189     for (Iterator i = slist.iterator(); i.hasNext();)
190     {
191       SRRset rrset = (SRRset) i.next();
192       if (rrset.getType() == qtype) result.add(rrset);
193     }
194
195     return (SRRset[]) result.toArray(empty_srrset_array);
196   }
197
198   public void deleteRRset(SRRset rrset, int section)
199   {
200     List slist = getSectionList(section);
201
202     if (slist.size() == 0) return;
203
204     slist.remove(rrset);
205   }
206
207   public void clear(int section)
208   {
209     if (section < Section.QUESTION || section > Section.ADDITIONAL)
210       throw new IllegalArgumentException("Invalid section.");
211
212     if (section == Section.QUESTION)
213     {
214       mQuestion = null;
215       return;
216     }
217     if (section == Section.ADDITIONAL)
218     {
219       mOPTRecord = null;
220     }
221
222     mSection[section - 1] = null;
223   }
224
225   public void clear()
226   {
227     for (int s = Section.QUESTION; s <= Section.ADDITIONAL; s++)
228     {
229       clear(s);
230     }
231   }
232
233   public int getRcode()
234   {
235     // FIXME: might want to do what Message does and handle extended rcodes.
236     return mHeader.getRcode();
237   }
238
239   public int getStatus()
240   {
241     return mSecurityStatus.getStatus();
242   }
243
244   public void setStatus(byte status)
245   {
246     mSecurityStatus.setStatus(status);
247   }
248
249   public SecurityStatus getSecurityStatus()
250   {
251     return mSecurityStatus;
252   }
253   public void setSecurityStatus(SecurityStatus s)
254   {
255     if (s == null) return;
256     mSecurityStatus = s;
257   }
258   
259   public Message getMessage()
260   {
261     // Generate our new message.
262     Message m = new Message(mHeader.getID());
263
264     // Convert the header
265     // We do this for two reasons: 1) setCount() is package scope, so we can't
266     // do that, and 2) setting the header on a message after creating the
267     // message frequently gets stuff out of sync, leading to malformed wire
268     // format messages.
269     Header h = m.getHeader();
270     h.setOpcode(mHeader.getOpcode());
271     h.setRcode(mHeader.getRcode());
272     for (int i = 0; i < 16; i++)
273     {
274       if (Flags.isFlag(i)) {
275           if (mHeader.getFlag(i)) {
276               h.setFlag(i);
277           } else {
278               h.unsetFlag(i);
279           }
280       }
281     }
282
283     // Add all the records. -- this will set the counts correctly in the
284     // message header.
285
286     if (mQuestion != null)
287     {
288       m.addRecord(mQuestion, Section.QUESTION);
289     }
290
291     for (int sec = Section.ANSWER; sec <= Section.ADDITIONAL; sec++)
292     {
293       List slist = getSectionList(sec);
294       for (Iterator i = slist.iterator(); i.hasNext();)
295       {
296         SRRset rrset = (SRRset) i.next();
297         for (Iterator j = rrset.rrs(); j.hasNext();)
298         {
299           m.addRecord((Record) j.next(), sec);
300         }
301         for (Iterator j = rrset.sigs(); j.hasNext();)
302         {
303           m.addRecord((Record) j.next(), sec);
304         }
305       }
306     }
307
308     if (mOPTRecord != null)
309     {
310       m.addRecord(mOPTRecord, Section.ADDITIONAL);
311     }
312
313     return m;
314   }
315
316   public int getCount(int section)
317   {
318     if (section == Section.QUESTION)
319     {
320       return mQuestion == null ? 0 : 1;
321     }
322     List sectionList = getSectionList(section);
323     if (sectionList == null) return 0;
324     if (sectionList.size() == 0) return 0;
325
326     int count = 0;
327     for (Iterator i = sectionList.iterator(); i.hasNext(); )
328     {
329       SRRset sr = (SRRset) i.next();
330       count += sr.totalSize();
331     }
332     return count;
333   }
334   
335   public String toString()
336   {
337     return getMessage().toString();
338   }
339
340   /**
341    * Find a specific (S)RRset in a given section.
342    * 
343    * @param name the name of the RRset.
344    * @param type the type of the RRset.
345    * @param dclass the class of the RRset.
346    * @param section the section to look in (ANSWER -> ADDITIONAL)
347    * 
348    * @return The SRRset if found, null otherwise.
349    */
350   public SRRset findRRset(Name name, int type, int dclass, int section)
351   {
352     if (section <= Section.QUESTION || section > Section.ADDITIONAL)
353       throw new IllegalArgumentException("Invalid section.");
354
355     SRRset[] rrsets = getSectionRRsets(section);
356
357     for (int i = 0; i < rrsets.length; i++)
358     {
359       if (rrsets[i].getName().equals(name) && rrsets[i].getType() == type
360           && rrsets[i].getDClass() == dclass)
361       {
362         return rrsets[i];
363       }
364     }
365
366     return null;
367   }
368
369   /**
370    * Find an "answer" RRset. This will look for RRsets in the ANSWER section
371    * that match the <qname,qtype,qclass>, taking into consideration CNAMEs.
372    * 
373    * @param qname The starting search name.
374    * @param qtype The search type.
375    * @param qclass The search class.
376    * 
377    * @return a SRRset matching the query. This SRRset may have a different
378    *         name from qname, due to following a CNAME chain.
379    */
380   public SRRset findAnswerRRset(Name qname, int qtype, int qclass)
381   {
382     SRRset[] srrsets = getSectionRRsets(Section.ANSWER);
383
384     for (int i = 0; i < srrsets.length; i++)
385     {
386       if (srrsets[i].getName().equals(qname)
387           && srrsets[i].getType() == Type.CNAME)
388       {
389         CNAMERecord cname = (CNAMERecord) srrsets[i].first();
390         qname = cname.getTarget();
391         continue;
392       }
393
394       if (srrsets[i].getName().equals(qname) && srrsets[i].getType() == qtype
395           && srrsets[i].getDClass() == qclass)
396       {
397         return srrsets[i];
398       }
399     }
400
401     return null;
402   }
403
404 }