1 // Copyright 2003-2005 Arthur van Hoff, Rick Blair
2 // Licensed under Apache License version 2.0
3 // Original license LGPL
4 
5 package javax.jmdns.impl;
6 
7 import java.net.InetAddress;
8 import java.util.Set;
9 import java.util.logging.Level;
10 import java.util.logging.Logger;
11 
12 import javax.jmdns.ServiceInfo;
13 import javax.jmdns.ServiceInfo.Fields;
14 import javax.jmdns.impl.JmDNSImpl.ServiceTypeEntry;
15 import javax.jmdns.impl.constants.DNSConstants;
16 import javax.jmdns.impl.constants.DNSRecordClass;
17 import javax.jmdns.impl.constants.DNSRecordType;
18 
19 /**
20  * A DNS question.
21  *
22  * @author Arthur van Hoff, Pierre Frisch
23  */
24 public class DNSQuestion extends DNSEntry {
25     private static Logger logger = Logger.getLogger(DNSQuestion.class.getName());
26 
27     /**
28      * Address question.
29      */
30     private static class DNS4Address extends DNSQuestion {
DNS4Address(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique)31         DNS4Address(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique) {
32             super(name, type, recordClass, unique);
33         }
34 
35         @Override
addAnswers(JmDNSImpl jmDNSImpl, Set<DNSRecord> answers)36         public void addAnswers(JmDNSImpl jmDNSImpl, Set<DNSRecord> answers) {
37             DNSRecord answer = jmDNSImpl.getLocalHost().getDNSAddressRecord(this.getRecordType(), DNSRecordClass.UNIQUE, DNSConstants.DNS_TTL);
38             if (answer != null) {
39                 answers.add(answer);
40             }
41         }
42 
43         @Override
iAmTheOnlyOne(JmDNSImpl jmDNSImpl)44         public boolean iAmTheOnlyOne(JmDNSImpl jmDNSImpl) {
45             String name = this.getName().toLowerCase();
46             return jmDNSImpl.getLocalHost().getName().equals(name) || jmDNSImpl.getServices().keySet().contains(name);
47         }
48 
49     }
50 
51     /**
52      * Address question.
53      */
54     private static class DNS6Address extends DNSQuestion {
DNS6Address(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique)55         DNS6Address(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique) {
56             super(name, type, recordClass, unique);
57         }
58 
59         @Override
addAnswers(JmDNSImpl jmDNSImpl, Set<DNSRecord> answers)60         public void addAnswers(JmDNSImpl jmDNSImpl, Set<DNSRecord> answers) {
61             DNSRecord answer = jmDNSImpl.getLocalHost().getDNSAddressRecord(this.getRecordType(), DNSRecordClass.UNIQUE, DNSConstants.DNS_TTL);
62             if (answer != null) {
63                 answers.add(answer);
64             }
65         }
66 
67         @Override
iAmTheOnlyOne(JmDNSImpl jmDNSImpl)68         public boolean iAmTheOnlyOne(JmDNSImpl jmDNSImpl) {
69             String name = this.getName().toLowerCase();
70             return jmDNSImpl.getLocalHost().getName().equals(name) || jmDNSImpl.getServices().keySet().contains(name);
71         }
72 
73     }
74 
75     /**
76      * Host Information question.
77      */
78     private static class HostInformation extends DNSQuestion {
HostInformation(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique)79         HostInformation(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique) {
80             super(name, type, recordClass, unique);
81         }
82     }
83 
84     /**
85      * Pointer question.
86      */
87     private static class Pointer extends DNSQuestion {
Pointer(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique)88         Pointer(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique) {
89             super(name, type, recordClass, unique);
90         }
91 
92         @Override
addAnswers(JmDNSImpl jmDNSImpl, Set<DNSRecord> answers)93         public void addAnswers(JmDNSImpl jmDNSImpl, Set<DNSRecord> answers) {
94             // find matching services
95             for (ServiceInfo serviceInfo : jmDNSImpl.getServices().values()) {
96                 this.addAnswersForServiceInfo(jmDNSImpl, answers, (ServiceInfoImpl) serviceInfo);
97             }
98             if (this.isServicesDiscoveryMetaQuery()) {
99                 for (String serviceType : jmDNSImpl.getServiceTypes().keySet()) {
100                     ServiceTypeEntry typeEntry = jmDNSImpl.getServiceTypes().get(serviceType);
101                     answers.add(new DNSRecord.Pointer("_services._dns-sd._udp.local.", DNSRecordClass.CLASS_IN, DNSRecordClass.NOT_UNIQUE, DNSConstants.DNS_TTL, typeEntry.getType()));
102                 }
103             } else if (this.isReverseLookup()) {
104                 String ipValue = this.getQualifiedNameMap().get(Fields.Instance);
105                 if ((ipValue != null) && (ipValue.length() > 0)) {
106                     InetAddress address = jmDNSImpl.getLocalHost().getInetAddress();
107                     String hostIPAddress = (address != null ? address.getHostAddress() : "");
108                     if (ipValue.equalsIgnoreCase(hostIPAddress)) {
109                         if (this.isV4ReverseLookup()) {
110                             answers.add(jmDNSImpl.getLocalHost().getDNSReverseAddressRecord(DNSRecordType.TYPE_A, DNSRecordClass.NOT_UNIQUE, DNSConstants.DNS_TTL));
111                         }
112                         if (this.isV6ReverseLookup()) {
113                             answers.add(jmDNSImpl.getLocalHost().getDNSReverseAddressRecord(DNSRecordType.TYPE_AAAA, DNSRecordClass.NOT_UNIQUE, DNSConstants.DNS_TTL));
114                         }
115                     }
116                 }
117             } else if (this.isDomainDiscoveryQuery()) {
118                 // FIXME [PJYF Nov 16 2010] We do not currently support domain discovery
119             }
120         }
121 
122     }
123 
124     /**
125      * Service question.
126      */
127     private static class Service extends DNSQuestion {
Service(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique)128         Service(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique) {
129             super(name, type, recordClass, unique);
130         }
131 
132         @Override
addAnswers(JmDNSImpl jmDNSImpl, Set<DNSRecord> answers)133         public void addAnswers(JmDNSImpl jmDNSImpl, Set<DNSRecord> answers) {
134             String loname = this.getName().toLowerCase();
135             if (jmDNSImpl.getLocalHost().getName().equalsIgnoreCase(loname)) {
136                 // type = DNSConstants.TYPE_A;
137                 answers.addAll(jmDNSImpl.getLocalHost().answers(this.isUnique(), DNSConstants.DNS_TTL));
138                 return;
139             }
140             // Service type request
141             if (jmDNSImpl.getServiceTypes().containsKey(loname)) {
142                 DNSQuestion question = new Pointer(this.getName(), DNSRecordType.TYPE_PTR, this.getRecordClass(), this.isUnique());
143                 question.addAnswers(jmDNSImpl, answers);
144                 return;
145             }
146 
147             this.addAnswersForServiceInfo(jmDNSImpl, answers, (ServiceInfoImpl) jmDNSImpl.getServices().get(loname));
148         }
149 
150         @Override
iAmTheOnlyOne(JmDNSImpl jmDNSImpl)151         public boolean iAmTheOnlyOne(JmDNSImpl jmDNSImpl) {
152             String name = this.getName().toLowerCase();
153             return jmDNSImpl.getLocalHost().getName().equals(name) || jmDNSImpl.getServices().keySet().contains(name);
154         }
155 
156     }
157 
158     /**
159      * Text question.
160      */
161     private static class Text extends DNSQuestion {
Text(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique)162         Text(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique) {
163             super(name, type, recordClass, unique);
164         }
165 
166         @Override
addAnswers(JmDNSImpl jmDNSImpl, Set<DNSRecord> answers)167         public void addAnswers(JmDNSImpl jmDNSImpl, Set<DNSRecord> answers) {
168             this.addAnswersForServiceInfo(jmDNSImpl, answers, (ServiceInfoImpl) jmDNSImpl.getServices().get(this.getName().toLowerCase()));
169         }
170 
171         @Override
iAmTheOnlyOne(JmDNSImpl jmDNSImpl)172         public boolean iAmTheOnlyOne(JmDNSImpl jmDNSImpl) {
173             String name = this.getName().toLowerCase();
174             return jmDNSImpl.getLocalHost().getName().equals(name) || jmDNSImpl.getServices().keySet().contains(name);
175         }
176 
177     }
178 
179     /**
180      * AllRecords question.
181      */
182     private static class AllRecords extends DNSQuestion {
AllRecords(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique)183         AllRecords(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique) {
184             super(name, type, recordClass, unique);
185         }
186 
187         @Override
isSameType(DNSEntry entry)188         public boolean isSameType(DNSEntry entry) {
189             // We match all non null entry
190             return (entry != null);
191         }
192 
193         @Override
addAnswers(JmDNSImpl jmDNSImpl, Set<DNSRecord> answers)194         public void addAnswers(JmDNSImpl jmDNSImpl, Set<DNSRecord> answers) {
195             String loname = this.getName().toLowerCase();
196             if (jmDNSImpl.getLocalHost().getName().equalsIgnoreCase(loname)) {
197                 // type = DNSConstants.TYPE_A;
198                 answers.addAll(jmDNSImpl.getLocalHost().answers(this.isUnique(), DNSConstants.DNS_TTL));
199                 return;
200             }
201             // Service type request
202             if (jmDNSImpl.getServiceTypes().containsKey(loname)) {
203                 DNSQuestion question = new Pointer(this.getName(), DNSRecordType.TYPE_PTR, this.getRecordClass(), this.isUnique());
204                 question.addAnswers(jmDNSImpl, answers);
205                 return;
206             }
207 
208             this.addAnswersForServiceInfo(jmDNSImpl, answers, (ServiceInfoImpl) jmDNSImpl.getServices().get(loname));
209         }
210 
211         @Override
iAmTheOnlyOne(JmDNSImpl jmDNSImpl)212         public boolean iAmTheOnlyOne(JmDNSImpl jmDNSImpl) {
213             String name = this.getName().toLowerCase();
214             return jmDNSImpl.getLocalHost().getName().equals(name) || jmDNSImpl.getServices().keySet().contains(name);
215         }
216 
217     }
218 
DNSQuestion(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique)219     DNSQuestion(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique) {
220         super(name, type, recordClass, unique);
221     }
222 
223     /**
224      * Create a question.
225      *
226      * @param name
227      *            DNS name to be resolved
228      * @param type
229      *            Record type to resolve
230      * @param recordClass
231      *            Record class to resolve
232      * @param unique
233      *            Request unicast response (Currently not supported in this implementation)
234      * @return new question
235      */
newQuestion(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique)236     public static DNSQuestion newQuestion(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique) {
237         switch (type) {
238             case TYPE_A:
239                 return new DNS4Address(name, type, recordClass, unique);
240             case TYPE_A6:
241                 return new DNS6Address(name, type, recordClass, unique);
242             case TYPE_AAAA:
243                 return new DNS6Address(name, type, recordClass, unique);
244             case TYPE_ANY:
245                 return new AllRecords(name, type, recordClass, unique);
246             case TYPE_HINFO:
247                 return new HostInformation(name, type, recordClass, unique);
248             case TYPE_PTR:
249                 return new Pointer(name, type, recordClass, unique);
250             case TYPE_SRV:
251                 return new Service(name, type, recordClass, unique);
252             case TYPE_TXT:
253                 return new Text(name, type, recordClass, unique);
254             default:
255                 return new DNSQuestion(name, type, recordClass, unique);
256         }
257     }
258 
259     /**
260      * Check if this question is answered by a given DNS record.
261      */
answeredBy(DNSEntry rec)262     boolean answeredBy(DNSEntry rec) {
263         return this.isSameRecordClass(rec) && this.isSameType(rec) && this.getName().equals(rec.getName());
264     }
265 
266     /**
267      * Adds answers to the list for our question.
268      *
269      * @param jmDNSImpl
270      *            DNS holding the records
271      * @param answers
272      *            List of previous answer to append.
273      */
addAnswers(JmDNSImpl jmDNSImpl, Set<DNSRecord> answers)274     public void addAnswers(JmDNSImpl jmDNSImpl, Set<DNSRecord> answers) {
275         // By default we do nothing
276     }
277 
addAnswersForServiceInfo(JmDNSImpl jmDNSImpl, Set<DNSRecord> answers, ServiceInfoImpl info)278     protected void addAnswersForServiceInfo(JmDNSImpl jmDNSImpl, Set<DNSRecord> answers, ServiceInfoImpl info) {
279         if ((info != null) && info.isAnnounced()) {
280             if (this.getName().equalsIgnoreCase(info.getQualifiedName()) || this.getName().equalsIgnoreCase(info.getType())) {
281                 answers.addAll(jmDNSImpl.getLocalHost().answers(DNSRecordClass.UNIQUE, DNSConstants.DNS_TTL));
282                 answers.addAll(info.answers(DNSRecordClass.UNIQUE, DNSConstants.DNS_TTL, jmDNSImpl.getLocalHost()));
283             }
284             if (logger.isLoggable(Level.FINER)) {
285                 logger.finer(jmDNSImpl.getName() + " DNSQuestion(" + this.getName() + ").addAnswersForServiceInfo(): info: " + info + "\n" + answers);
286             }
287         }
288     }
289 
290     /*
291      * (non-Javadoc)
292      * @see javax.jmdns.impl.DNSEntry#isStale(long)
293      */
294     @Override
isStale(long now)295     public boolean isStale(long now) {
296         return false;
297     }
298 
299     /*
300      * (non-Javadoc)
301      * @see javax.jmdns.impl.DNSEntry#isExpired(long)
302      */
303     @Override
isExpired(long now)304     public boolean isExpired(long now) {
305         return false;
306     }
307 
308     /**
309      * Checks if we are the only to be able to answer that question.
310      *
311      * @param jmDNSImpl
312      *            DNS holding the records
313      * @return <code>true</code> if we are the only one with the answer to the question, <code>false</code> otherwise.
314      */
iAmTheOnlyOne(JmDNSImpl jmDNSImpl)315     public boolean iAmTheOnlyOne(JmDNSImpl jmDNSImpl) {
316         return false;
317     }
318 
319     /*
320      * (non-Javadoc)
321      * @see javax.jmdns.impl.DNSEntry#toString(java.lang.StringBuilder)
322      */
323     @Override
toString(StringBuilder aLog)324     public void toString(StringBuilder aLog) {
325         // do nothing
326     }
327 
328 }