Developer Area

root/apps/iphone/superbook/trunk/DotTel_SDK/Classes/DnsResolver.m @ 626

Revision 626, 10.9 kB (checked in by henri, 4 years ago)

App-wide bug fixing based on static analyser report

Line 
1//
2//  DnsResolver.m
3//  DotTel SDK
4//
5//  Created by Henri Asseily on 9/14/08.
6/*
7 Copyright (c) 2008-2009, Telnic Ltd. All rights reserved.
8 
9 Redistribution and use in source and binary forms, with or without modification,
10 are permitted provided that the following conditions are met:
11 
12 Redistributions of source code must retain the above copyright notice, this list of conditions
13 and the following disclaimer. Redistributions in binary form must reproduce the above copyright
14 notice, this list of conditions and the following disclaimer in the documentation and/or other
15 materials provided with the distribution.
16 Neither the name of the Telnic Ltd. nor the names of its contributors may be used to endorse or
17 promote products derived from this software without specific prior written permission.
18 THIS DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
19 OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
20 AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
24 IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27//
28
29#import "DnsResolver.h"
30
31@interface DnsResolver (PrivateMethods)
32
33- (ldns_resolver *)createLdnsResolver;
34- (ldns_rr_list *)retrieveResourceRecordsOfType:(ldns_rr_type)rrType fromDomain:(NSString *)domain withError:(NSError **)error;
35
36@end
37
38@implementation DnsResolver
39
40- (id)init {
41        self = [super init];
42        res = [self createLdnsResolver];
43        return self;
44}
45
46- (void)dealloc {
47        ldns_resolver_deep_free(res);
48        [super dealloc];
49}
50
51- (NSUInteger)getNAPTRForTel:(NSString *)domain inArray:(NSMutableArray *)naptrArray withError:(NSError **)error {
52#ifdef DEBUG
53        NSLog(@"Domain %@: Retrieving NAPTR Records", domain);
54#endif
55        [naptrArray retain];
56        ldns_rr_list *naptrs = [self retrieveResourceRecordsOfType:LDNS_RR_TYPE_NAPTR fromDomain:domain withError:error];
57        if (!naptrs) {
58                [naptrArray release];
59                return 0;
60        }
61       
62        NSDate *lookupDate = [[NSDate date] retain];
63        NSUInteger i;
64        NSUInteger count = ldns_rr_list_rr_count(naptrs);
65       
66        for (i = 0; i < count; i++) {
67                RecordNaptr *theRec = [RecordNaptr recordWithRr:ldns_rr_list_rr(naptrs, i) date:lookupDate];
68                if (theRec.isValid)
69                        [naptrArray addObject:theRec];
70        }
71       
72        [naptrArray sortUsingSelector:@selector(comparator:)];
73       
74        //[naptrArray sortUsingFunction:naptrSortFunction context:NULL];
75       
76        ldns_rr_list_deep_free(naptrs);
77        [lookupDate release];
78        [naptrArray release];
79        return count;
80       
81}
82
83// TODO: header will be made obsolete by NINFO
84- (NSUInteger)getTXTForTel:(NSString *)domain inArray:(NSMutableArray *)txtArray withError:(NSError **)error {
85               
86#ifdef DEBUG
87        NSLog(@"Domain %@: Retrieving TXT Records", domain);
88#endif
89        ldns_rr_list *txts = [self retrieveResourceRecordsOfType:LDNS_RR_TYPE_TXT fromDomain:domain withError:error];
90        if (!txts) {
91                return 0;
92        }
93       
94        NSDate *lookupDate = [[NSDate date] retain];
95        NSUInteger i;
96        NSUInteger count = ldns_rr_list_rr_count(txts);
97       
98        for (i = 0; i < count; i++) {
99                RecordTxt *theRec = [RecordTxt recordWithRr:ldns_rr_list_rr(txts, i) date:lookupDate];
100                if (theRec.isValid) {
101                        [txtArray addObject:theRec];
102                }
103        }
104       
105        ldns_rr_list_deep_free(txts);
106        [lookupDate release];
107       
108        return count;
109       
110}
111
112- (NSUInteger)getLOCForTel:(NSString *)domain inArray:(NSMutableArray *)locArray withError:(NSError **)error {
113       
114        // Note that there should only be ***ONE AND ONLY ONE*** LOC record per subdomain
115        // The DNS call gets them all, but we pick the first one (which is random and
116        // could be different on subsequent DNS calls)
117        // It's the responsibility of the caller to make sure the array is sent empty
118               
119#ifdef DEBUG
120        NSLog(@"Domain %@: Retrieving LOC Records", domain);
121#endif
122        ldns_rr_list *locs = [self retrieveResourceRecordsOfType:LDNS_RR_TYPE_LOC fromDomain:domain withError:error];
123        if (!locs) {
124                return 0;
125        }
126       
127        NSDate *lookupDate = [[NSDate date] retain];
128        NSUInteger count = ldns_rr_list_rr_count(locs);
129        RecordLoc *theRec = [RecordLoc recordWithRr:ldns_rr_list_rr(locs, 0) date:lookupDate];
130        if (theRec.isValid)
131                [locArray addObject:theRec];
132       
133        ldns_rr_list_deep_free(locs);
134        [lookupDate release];
135       
136        return count;
137       
138}
139
140- (NSIndexSet *)updateNaptrArray:(NSMutableArray *)naptrArray withTxtArray:(NSArray *)txtArray {
141#ifdef DEBUG
142        NSLog(@"Updating NAPTR array with TXT array");
143#endif
144        NSMutableIndexSet *updatedNaptrs = [NSMutableIndexSet indexSet];
145        if ((naptrArray == nil) || (txtArray == nil)) return updatedNaptrs;
146        if (([naptrArray count] == 0) || ([txtArray count] == 0)) return updatedNaptrs;
147        RecordNaptr *aN;
148        RecordTxt *aT;
149       
150        // Create a dictionary to map order+prefs of txt
151        // We're doing the mapping on the txt array because we'll need to iterate
152        // on the naptr array to populate the index set
153        NSMutableDictionary *txtDict = [NSMutableDictionary dictionaryWithCapacity:[txtArray count]];
154        for (aT in txtArray) {
155                if (aT.subType == RecordTxtSubTypeLabel) {
156                        NSString *key = [NSString stringWithFormat:@"%d_%d", aT.order, aT.preference];
157                        [txtDict setObject:aT forKey:key];
158                }
159        }
160       
161        NSUInteger i, count = [naptrArray count];
162        for (i = 0; i < count; i++) {
163                aN = [naptrArray objectAtIndex:i];
164                NSString *key = [NSString stringWithFormat:@"%d_%d", [aN.order intValue], [aN.preference intValue]];
165                aT = [txtDict objectForKey:key];
166                if (aT != nil) {
167                        aN.labelDescription = aT.textValue;
168                        [updatedNaptrs addIndex:i];
169                }
170        }
171        return updatedNaptrs;
172}
173
174#pragma mark ------------ private methods -------------------
175
176- (ldns_resolver *)createLdnsResolver {
177        ldns_resolver *resolver;
178        ldns_pkt *p;
179        ldns_status s;
180       
181        p = NULL;
182        resolver = NULL;
183       
184        /* create a new resolver from resolv.conf */
185        /* on the iphone we may not have a resolv.conf so we provide one */
186       
187        NSString *resolverFilePath = [[NSBundle mainBundle] pathForResource:@"resolv" ofType:@"conf"];
188       
189        s = ldns_resolver_new_frm_file(&resolver, [resolverFilePath cStringUsingEncoding:NSASCIIStringEncoding]);
190        if (s == LDNS_STATUS_OK) {
191#ifdef DEBUG
192                ldns_resolver_set_debug(resolver, TRUE);
193#endif
194                // Set ldns defaults. Here's how it'll work:
195               
196                ldns_resolver_set_usevc(resolver, TRUE);                        // use TCP by default for now. TODO: look at UDP behavior in GPRS/EDGE
197                ldns_resolver_set_edns_udp_size(resolver, 4096);        // with a 4k max packet size when in UDP EDNS0
198                ldns_resolver_set_fallback(resolver, TRUE);                     // fallback to EDNS0 UDP, and then to TCP
199                ldns_resolver_set_fail(resolver, FALSE);                        // do not allow to fail to next nameserver
200                ldns_resolver_set_retry(resolver, 4);                           // Max number of retries
201                ldns_resolver_set_retrans(resolver, 2);                         // Seconds between retries
202                return resolver;
203        } else {
204                return NULL;
205        }
206}
207
208- (ldns_rr_list *)retrieveResourceRecordsOfType:(ldns_rr_type)rrType fromDomain:(NSString *)domain withError:(NSError **)error {
209       
210        ldns_rr_list *rrlist;   
211        ldns_rdf *ldnsdomain = ldns_dname_new_frm_str([[self idnStringFromDomain:domain] UTF8String]);
212        NSError *err2;
213        NSDictionary *dict;
214       
215        if (!ldnsdomain) {
216#ifdef DEBUG
217                NSLog(@"Domain %@: Can't do ldns_dname_new_frm_str", domain);
218#endif
219                dict = [NSDictionary dictionaryWithObject:NSLocalizedString(@"DNS Error: Can't parse resolver name", @"DNS Error resolver parsing") forKey:NSLocalizedDescriptionKey];
220                err2 = [NSError errorWithDomain:@"org.telnic.tel.sdk" code:RESOLVER_STATUS_NO_NAME userInfo:dict];
221                if (error)
222                        *error = err2;
223                return NULL;
224        }
225       
226        ldns_pkt *p;
227       
228//      @synchronized([self class]) {
229                       
230        p = ldns_resolver_query(res,
231                                                        ldnsdomain,
232                                                        rrType,
233                                                        LDNS_RR_CLASS_IN,
234                                                        LDNS_RD);
235
236        ldns_rdf_free(ldnsdomain);
237
238        // Have to play the switch game to encapsulate everything in one potentially synchronized block, with one return statement
239        switch (1) {
240                case 1: // always check for existence of p
241                        if (!p)  {
242#ifdef DEBUG
243                                NSLog(@"Domain %@: Can't do ldns_resolver_query (rr type %d)", domain, rrType);
244#endif
245                                dict = [NSDictionary dictionaryWithObject:NSLocalizedString(@"Network Error: No Internet Connection", @"Network error, no net connect") forKey:NSLocalizedDescriptionKey];
246                                err2 = [NSError errorWithDomain:@"org.telnic.tel.sdk" code:RESOLVER_STATUS_NO_CONNECTION userInfo:dict];
247                                if (error)
248                                        *error = err2;
249                                // recreate a resolver for later
250                                ldns_resolver_deep_free(res);
251                                res = [self createLdnsResolver];
252                                rrlist = NULL;
253                                break;
254                        }
255                case 2: // p exists, check for existence of rrlist answer
256                        /* retrieve the records from the answer section of that
257                         * packet
258                         */
259                        rrlist = ldns_pkt_rr_list_by_type(p,
260                                                                                          rrType,
261                                                                                          LDNS_SECTION_ANSWER);
262                        if (!rrlist) {
263#ifdef DEBUG
264                                NSLog(@"Domain %@: invalid answer name after query (rr type %d)", domain, rrType);
265#endif
266                                if (ldns_pkt_get_rcode(p) == LDNS_RCODE_NXDOMAIN) {
267                                        dict = [NSDictionary dictionaryWithObject:NSLocalizedString(@"Domain does not exist", @"Non-existent domain") forKey:NSLocalizedDescriptionKey];
268                                        err2 = [NSError errorWithDomain:@"org.telnic.tel.sdk" code:RESOLVER_STATUS_NXDOMAIN userInfo:dict];
269                                } else {
270                                        dict = [NSDictionary dictionaryWithObject:NSLocalizedString(@"Data Error: Invalid answer", @"Data Error: Invalid answer") forKey:NSLocalizedDescriptionKey];
271                                        err2 = [NSError errorWithDomain:@"org.telnic.tel.sdk" code:RESOLVER_STATUS_INVALID_ANSWER userInfo:dict];
272                                }
273                                if (error)
274                                        *error = err2;
275                                rrlist = NULL;
276                                ldns_pkt_free(p);
277                                break;
278                        }
279                case 3: // rrlist exists, make sure it's ok
280                        @try {
281                                ldns_rr_list_rr_count(rrlist);
282                        }
283                        @catch (NSException * e) {
284#ifdef DEBUG
285                                NSLog(@"Domain %@: ldns_rr_list_rr_count exception caught! (rr type %d)", domain, rrType);
286#endif
287                                // Exception is caught if there's no data in the rrlist,
288                                // i.e. if there are no records. That's BAD.
289                                // TODO: See in ldns rr.c source why ldns_rr_list_rr_count() fails and doesn't return 0
290                                // In the meantime, disable the error
291                               
292//                              dict = [NSDictionary dictionaryWithObject:NSLocalizedString(@"Network Error: Bad data returned", @"Network Error: Bad data returned") forKey:NSLocalizedDescriptionKey];
293//                              err2 = [NSError errorWithDomain:@"org.telnic.tel.sdk" code:RESOLVER_STATUS_BAD_DATA userInfo:dict];
294//                              if (error)
295//                                      *error = err2;
296                                rrlist = NULL;
297                        }
298                        ldns_pkt_free(p);                       
299                        break;
300                default:
301                        break;
302        }
303//      } // end @synchronized
304               
305        return rrlist;
306}
307
308#pragma mark -
309#pragma mark IDN methods
310
311- (NSString *)idnStringFromDomain:(NSString *)domain {
312        if (domain == nil) return nil;
313        char *p;
314        int rc;
315        rc = idna_to_ascii_8z([domain UTF8String], &p, 0);
316        if (rc != IDNA_SUCCESS) {
317                NSLog(@"idnStringFromDomain failed (%d): %s\n", rc, idna_strerror (rc));
318                return domain;
319    }
320        NSString *idnString = [NSString stringWithCString:p encoding:NSASCIIStringEncoding];
321        free(p);
322        return idnString;
323}
324
325@end
Note: See TracBrowser for help on using the browser.
Telnic
Search This Site
Partners
Neustar
ICANN
Main site | WHOIS | Sell .tel | FAQ | Archived Site | About Telnic | Contact Us