1// Copyright 2003-2005 Arthur van Hoff, Rick Blair
2// Licensed under Apache License version 2.0
3// Original license LGPL
4
5package javax.jmdns.impl;
6
7import java.net.InetAddress;
8import java.util.Set;
9import java.util.logging.Level;
10import java.util.logging.Logger;
11
12import javax.jmdns.ServiceInfo;
13import javax.jmdns.ServiceInfo.Fields;
14import javax.jmdns.impl.JmDNSImpl.ServiceTypeEntry;
15import javax.jmdns.impl.constants.DNSConstants;
16import javax.jmdns.impl.constants.DNSRecordClass;
17import javax.jmdns.impl.constants.DNSRecordType;
18
19/**
20 * A DNS question.
21 *
22 * @author Arthur van Hoff, Pierre Frisch
23 */
24public 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 {
31        DNS4Address(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique) {
32            super(name, type, recordClass, unique);
33        }
34
35        @Override
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
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 {
55        DNS6Address(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique) {
56            super(name, type, recordClass, unique);
57        }
58
59        @Override
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
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 {
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 {
88        Pointer(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique) {
89            super(name, type, recordClass, unique);
90        }
91
92        @Override
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 {
128        Service(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique) {
129            super(name, type, recordClass, unique);
130        }
131
132        @Override
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
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 {
162        Text(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique) {
163            super(name, type, recordClass, unique);
164        }
165
166        @Override
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
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 {
183        AllRecords(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique) {
184            super(name, type, recordClass, unique);
185        }
186
187        @Override
188        public boolean isSameType(DNSEntry entry) {
189            // We match all non null entry
190            return (entry != null);
191        }
192
193        @Override
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
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
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     */
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     */
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     */
274    public void addAnswers(JmDNSImpl jmDNSImpl, Set<DNSRecord> answers) {
275        // By default we do nothing
276    }
277
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
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
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     */
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
324    public void toString(StringBuilder aLog) {
325        // do nothing
326    }
327
328}