Coverage Report

Created: 2025-03-09 06:50

/src/openweave-core/src/inet/DNSResolver.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 *
3
 *    Copyright (c) 2013-2017 Nest Labs, Inc.
4
 *    All rights reserved.
5
 *
6
 *    Licensed under the Apache License, Version 2.0 (the "License");
7
 *    you may not use this file except in compliance with the License.
8
 *    You may obtain a copy of the License at
9
 *
10
 *        http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 *    Unless required by applicable law or agreed to in writing, software
13
 *    distributed under the License is distributed on an "AS IS" BASIS,
14
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 *    See the License for the specific language governing permissions and
16
 *    limitations under the License.
17
 */
18
19
/**
20
 *    @file
21
 *      This file implements DNSResolver, the object that abstracts
22
 *      Domain Name System (DNS) resolution in InetLayer.
23
 *
24
 */
25
26
#include <InetLayer/InetLayer.h>
27
#include <InetLayer/DNSResolver.h>
28
#include <InetLayer/InetLayerEvents.h>
29
30
#include <Weave/Support/CodeUtils.h>
31
32
#include <string.h>
33
34
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
35
#include <lwip/init.h>
36
#include <lwip/dns.h>
37
#include <lwip/tcpip.h>
38
39
#ifndef LWIP_DNS_FOUND_CALLBACK_TYPE
40
#define LWIP_DNS_FOUND_CALLBACK_TYPE    dns_found_callback
41
#endif
42
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
43
44
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
45
#include <netdb.h>
46
#include <errno.h>
47
#include <stdlib.h>
48
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
49
50
namespace nl {
51
namespace Inet {
52
53
Weave::System::ObjectPool<DNSResolver, INET_CONFIG_NUM_DNS_RESOLVERS> DNSResolver::sPool;
54
55
/**
56
 *  This method revolves a host name into a list of IP addresses.
57
 *
58
 *  @note
59
 *     Even if the operation completes successfully,
60
 *     the result might be a zero-length list of IP addresses.
61
 *     Most of the error generated are returned via the
62
 *     application callback.
63
 *
64
 *  @param[in]  hostName    A pointer to a C string representing the host name
65
 *                          to be queried.
66
 *  @param[in]  hostNameLen The string length of host name.
67
 *  @param[in]  maxAddrs    The maximum number of addresses to store in the DNS
68
 *                          table.
69
 *  @param[in]  options     An integer value controlling how host name address
70
 *                          resolution is performed.  Values are from the #DNSOptions
71
 *                          enumeration.
72
 *  @param[in]  addrArray   A pointer to the DNS table.
73
 *  @param[in]  onComplete  A pointer to the callback function when a DNS
74
 *                          request is complete.
75
 *  @param[in]  appState    A pointer to the application state to be passed to
76
 *                          onComplete when a DNS request is complete.
77
 *
78
 *  @retval INET_NO_ERROR                   if a DNS request is handled
79
 *                                          successfully.
80
 *
81
 *  @retval INET_ERROR_NOT_IMPLEMENTED      if DNS resolution is not enabled on
82
 *                                          the underlying platform.
83
 *
84
 *  @retval _other_                         if other POSIX network or OS error
85
 *                                          was returned by the underlying DNS
86
 *                                          resolver implementation.
87
 *
88
 */
89
INET_ERROR DNSResolver::Resolve(const char *hostName, uint16_t hostNameLen, uint8_t options,
90
        uint8_t maxAddrs, IPAddress *addrArray,
91
        DNSResolver::OnResolveCompleteFunct onComplete, void *appState)
92
0
{
93
0
    INET_ERROR res = INET_NO_ERROR;
94
95
#if !WEAVE_SYSTEM_CONFIG_USE_SOCKETS && !LWIP_DNS
96
    Release();
97
    return INET_ERROR_NOT_IMPLEMENTED;
98
#endif // !WEAVE_SYSTEM_CONFIG_USE_SOCKETS && !LWIP_DNS
99
100
0
    uint8_t addrFamilyOption = (options & kDNSOption_AddrFamily_Mask);
101
0
    uint8_t optionFlags = (options & kDNSOption_Flags_Mask);
102
103
    // Check that the supplied options are valid.
104
0
    if ((addrFamilyOption != kDNSOption_AddrFamily_Any &&
105
0
         addrFamilyOption != kDNSOption_AddrFamily_IPv4Only &&
106
0
         addrFamilyOption != kDNSOption_AddrFamily_IPv4Preferred &&
107
0
         addrFamilyOption != kDNSOption_AddrFamily_IPv6Only &&
108
0
         addrFamilyOption != kDNSOption_AddrFamily_IPv6Preferred) ||
109
0
        (optionFlags & ~kDNSOption_ValidFlags) != 0)
110
0
    {
111
0
        Release();
112
0
        return INET_ERROR_BAD_ARGS;
113
0
    }
114
115
0
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS || (WEAVE_SYSTEM_CONFIG_USE_LWIP && LWIP_DNS)
116
117
    // TODO: Eliminate the need for a local buffer when running on LwIP by changing
118
    // the LwIP DNS interface to support non-nul terminated strings.
119
120
0
    char hostNameBuf[NL_DNS_HOSTNAME_MAX_LEN + 1]; // DNS limits hostnames to 253 max characters.
121
122
0
    memcpy(hostNameBuf, hostName, hostNameLen);
123
0
    hostNameBuf[hostNameLen] = 0;
124
125
0
    AppState = appState;
126
0
    AddrArray = addrArray;
127
0
    MaxAddrs = maxAddrs;
128
0
    NumAddrs = 0;
129
0
    DNSOptions = options;
130
0
    OnComplete = onComplete;
131
132
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
133
134
#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5
135
136
    u8_t lwipAddrType;
137
138
#if INET_CONFIG_ENABLE_IPV4
139
    switch (addrFamilyOption)
140
    {
141
    case kDNSOption_AddrFamily_IPv4Only:
142
        lwipAddrType = LWIP_DNS_ADDRTYPE_IPV4;
143
        break;
144
    case kDNSOption_AddrFamily_Any:
145
    case kDNSOption_AddrFamily_IPv4Preferred:
146
        lwipAddrType = LWIP_DNS_ADDRTYPE_IPV4_IPV6;
147
        break;
148
    case kDNSOption_AddrFamily_IPv6Only:
149
        lwipAddrType = LWIP_DNS_ADDRTYPE_IPV6;
150
        break;
151
    case kDNSOption_AddrFamily_IPv6Preferred:
152
        lwipAddrType = LWIP_DNS_ADDRTYPE_IPV6_IPV4;
153
        break;
154
    default:
155
        WeaveDie();
156
    }
157
#else // INET_CONFIG_ENABLE_IPV4
158
    lwipAddrType = LWIP_DNS_ADDRTYPE_IPV6;
159
#endif // INET_CONFIG_ENABLE_IPV4
160
161
#else // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR < 5
162
163
#if INET_CONFIG_ENABLE_IPV4
164
    if (addrFamilyOption == kDNSOption_AddrFamily_IPv6Only)
165
#endif
166
    {
167
        Release();
168
        return INET_ERROR_NOT_IMPLEMENTED;
169
    }
170
171
#endif // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR < 5
172
173
    // Lock LwIP stack
174
    LOCK_TCPIP_CORE();
175
176
    ip_addr_t lwipAddr;
177
    LWIP_DNS_FOUND_CALLBACK_TYPE lwipCallback = reinterpret_cast<LWIP_DNS_FOUND_CALLBACK_TYPE>(LwIPHandleResolveComplete);
178
179
    err_t lwipErr =
180
#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5
181
            dns_gethostbyname_addrtype(hostNameBuf, &lwipAddr, lwipCallback, this, lwipAddrType);
182
#else
183
            dns_gethostbyname(hostNameBuf, &lwipAddr, lwipCallback, this);
184
#endif
185
186
    // Unlock LwIP stack
187
    UNLOCK_TCPIP_CORE();
188
189
    if (lwipErr == ERR_OK)
190
    {
191
        Weave::System::Layer& lSystemLayer = SystemLayer();
192
193
#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5
194
        AddrArray[0] = IPAddress::FromLwIPAddr(lwipAddr);
195
#else
196
        AddrArray[0] = IPAddress::FromIPv4(lwipAddr);
197
#endif
198
        NumAddrs = 1;
199
200
        lSystemLayer.PostEvent(*this, kInetEvent_DNSResolveComplete, 0);
201
    }
202
    else if (lwipErr != ERR_INPROGRESS)
203
    {
204
        res = Weave::System::MapErrorLwIP(lwipErr);
205
        Release();
206
    }
207
208
    return res;
209
210
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
211
212
0
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
213
214
0
    struct addrinfo gaiHints;
215
0
    struct addrinfo * gaiResults = NULL;
216
0
    int gaiReturnCode;
217
218
    // Configure the hints argument for getaddrinfo()
219
0
    InitAddrInfoHints(gaiHints);
220
221
    // Call getaddrinfo() to perform the name resolution.
222
0
    gaiReturnCode = getaddrinfo(hostNameBuf, NULL, &gaiHints, &gaiResults);
223
224
    // Process the return code and results list returned by getaddrinfo(). If the call
225
    // was successful this will copy the resultant addresses into the caller's array.
226
0
    res = ProcessGetAddrInfoResult(gaiReturnCode, gaiResults);
227
228
    // Invoke the caller's completion function.
229
0
    onComplete(appState, res, NumAddrs, addrArray);
230
231
    // Release DNSResolver object.
232
0
    Release();
233
234
0
    return INET_NO_ERROR;
235
236
0
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
237
0
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS || (WEAVE_SYSTEM_CONFIG_USE_LWIP && LWIP_DNS)
238
0
}
239
240
241
/**
242
 *  This method cancels DNS requests that are in progress.
243
 *
244
 *  @retval INET_NO_ERROR.
245
 *
246
 */
247
INET_ERROR DNSResolver::Cancel()
248
0
{
249
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
250
251
    // NOTE: LwIP does not support canceling DNS requests that are in progress.  As a consequence,
252
    // we can't release the DNSResolver object until LwIP calls us back (because LwIP retains a
253
    // pointer to the DNSResolver object while the request is active).  However, now that the
254
    // application has called Cancel() we have to make sure to NOT call their OnComplete function
255
    // when the request completes.
256
    //
257
    // To ensure the right thing happens, we NULL the OnComplete pointer here, which signals the
258
    // code in HandleResolveComplete() and LwIPHandleResolveComplete() to not interact with the
259
    // application's state data (AddrArray) and to not call the application's callback. This has
260
    // to happen with the LwIP lock held, since LwIPHandleResolveComplete() runs on LwIP's thread.
261
262
    // Lock LwIP stack
263
    LOCK_TCPIP_CORE();
264
265
    // Signal that the request has been canceled by clearing the state of the resolver object.
266
    OnComplete = NULL;
267
    AddrArray = NULL;
268
    MaxAddrs = 0;
269
    NumAddrs = 0;
270
271
    // Unlock LwIP stack
272
    UNLOCK_TCPIP_CORE();
273
274
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
275
276
0
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
277
0
#if INET_CONFIG_ENABLE_ASYNC_DNS_SOCKETS
278
    // NOTE: DNS lookups can be canceled only when using the asynchronous mode.
279
280
0
    InetLayer& inet = Layer();
281
282
0
    OnComplete = NULL;
283
0
    AppState = NULL;
284
0
    inet.mAsyncDNSResolver.Cancel(*this);
285
286
0
#endif // INET_CONFIG_ENABLE_ASYNC_DNS_SOCKETS
287
0
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
288
289
0
    return INET_NO_ERROR;
290
0
}
291
292
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
293
294
/**
295
 *  This method is called by InetLayer on success, failure, or timeout of a
296
 *  DNS request.
297
 *
298
 */
299
void DNSResolver::HandleResolveComplete()
300
{
301
    // Call the application's completion handler if the request hasn't been canceled.
302
    if (OnComplete != NULL)
303
        OnComplete(AppState, (NumAddrs > 0) ? INET_NO_ERROR : INET_ERROR_HOST_NOT_FOUND, NumAddrs, AddrArray);
304
305
    // Release the resolver object.
306
    Release();
307
}
308
309
310
/**
311
 *  This method is called by LwIP network stack on success, failure, or timeout
312
 *  of a DNS request.
313
 *
314
 *  @param[in]  name            A pointer to a NULL-terminated C string
315
 *                              representing the host name that is queried.
316
 *  @param[in]  ipaddr          A pointer to a list of resolved IP addresses.
317
 *  @param[in]  callback_arg    A pointer to the arguments that are passed to
318
 *                              the callback function.
319
 *
320
 */
321
#if LWIP_VERSION_MAJOR > 1
322
void DNSResolver::LwIPHandleResolveComplete(const char *name, const ip_addr_t *ipaddr, void *callback_arg)
323
#else // LWIP_VERSION_MAJOR <= 1
324
void DNSResolver::LwIPHandleResolveComplete(const char *name, ip_addr_t *ipaddr, void *callback_arg)
325
#endif // LWIP_VERSION_MAJOR <= 1
326
{
327
    DNSResolver *resolver = (DNSResolver *)callback_arg;
328
329
    if (resolver != NULL)
330
    {
331
        Weave::System::Layer& lSystemLayer = resolver->SystemLayer();
332
333
        // Copy the resolved address to the application supplied buffer, but only if the request hasn't been canceled.
334
        if (resolver->OnComplete != NULL && ipaddr != NULL)
335
        {
336
#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5
337
            resolver->AddrArray[0] = IPAddress::FromLwIPAddr(*ipaddr);
338
#else
339
            resolver->AddrArray[0] = IPAddress::FromIPv4(*ipaddr);
340
#endif
341
            resolver->NumAddrs = 1;
342
        }
343
344
        lSystemLayer.PostEvent(*resolver, kInetEvent_DNSResolveComplete, 0);
345
    }
346
}
347
348
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
349
350
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
351
352
void DNSResolver::InitAddrInfoHints(struct addrinfo & hints)
353
0
{
354
0
    uint8_t addrFamilyOption = (DNSOptions & kDNSOption_AddrFamily_Mask);
355
356
0
    memset(&hints, 0, sizeof(hints));
357
0
#if INET_CONFIG_ENABLE_IPV4
358
0
    if (addrFamilyOption == kDNSOption_AddrFamily_IPv4Only)
359
0
    {
360
0
        hints.ai_family = AF_INET;
361
0
    }
362
0
    else if (addrFamilyOption == kDNSOption_AddrFamily_IPv6Only)
363
0
    {
364
0
        hints.ai_family = AF_INET6;
365
0
    }
366
0
    else
367
0
    {
368
0
        hints.ai_family = AF_UNSPEC;
369
0
    }
370
#else // INET_CONFIG_ENABLE_IPV4
371
    hints.ai_family = AF_INET6;
372
#endif // INET_CONFIG_ENABLE_IPV4
373
0
    hints.ai_flags = AI_ADDRCONFIG;
374
0
}
375
376
INET_ERROR DNSResolver::ProcessGetAddrInfoResult(int returnCode, struct addrinfo * results)
377
0
{
378
0
    INET_ERROR err = INET_NO_ERROR;
379
380
    // If getaddrinfo() succeeded, copy addresses in the returned addrinfo structures into the
381
    // application's output array...
382
0
    if (returnCode == 0)
383
0
    {
384
0
        NumAddrs = 0;
385
386
0
#if INET_CONFIG_ENABLE_IPV4
387
388
        // Based on the address family option specified by the application, determine which
389
        // types of addresses should be returned and the order in which they should appear.
390
0
        uint8_t addrFamilyOption = (DNSOptions & kDNSOption_AddrFamily_Mask);
391
0
        int primaryFamily, secondaryFamily;
392
0
        switch (addrFamilyOption)
393
0
        {
394
0
        case kDNSOption_AddrFamily_Any:
395
0
            primaryFamily = AF_UNSPEC;
396
0
            secondaryFamily = AF_UNSPEC;
397
0
            break;
398
0
        case kDNSOption_AddrFamily_IPv4Only:
399
0
            primaryFamily = AF_INET;
400
0
            secondaryFamily = AF_UNSPEC;
401
0
            break;
402
0
        case kDNSOption_AddrFamily_IPv4Preferred:
403
0
            primaryFamily = AF_INET;
404
0
            secondaryFamily = AF_INET6;
405
0
            break;
406
0
        case kDNSOption_AddrFamily_IPv6Only:
407
0
            primaryFamily = AF_INET6;
408
0
            secondaryFamily = AF_UNSPEC;
409
0
            break;
410
0
        case kDNSOption_AddrFamily_IPv6Preferred:
411
0
            primaryFamily = AF_INET6;
412
0
            secondaryFamily = AF_INET;
413
0
            break;
414
0
        default:
415
0
            WeaveDie();
416
0
        }
417
418
        // Determine the number of addresses of each family present in the results.
419
        // In the case of the secondary address family, only count these if they are
420
        // to be returned in the results.
421
0
        uint8_t numPrimaryAddrs = CountAddresses(primaryFamily, results);
422
0
        uint8_t numSecondaryAddrs = (secondaryFamily != AF_UNSPEC) ? CountAddresses(secondaryFamily, results) : 0;
423
0
        uint8_t numAddrs = numPrimaryAddrs + numSecondaryAddrs;
424
425
        // If the total number of addresses to be returned exceeds the application
426
        // specified max, ensure that at least 1 address from the secondary family
427
        // appears in the result (unless of course there are no such addresses or
428
        // the max is set to 1).
429
        // This ensures the application will try at least one secondary address
430
        // when attempting to communicate with the host.
431
0
        if (numAddrs > MaxAddrs && MaxAddrs > 1 && numPrimaryAddrs > 0 && numSecondaryAddrs > 0)
432
0
        {
433
0
            numPrimaryAddrs = ::nl::Weave::min(numPrimaryAddrs, (uint8_t)(MaxAddrs - 1));
434
0
        }
435
436
        // Copy the primary addresses into the beginning of the application's output array,
437
        // up to the limit determined above.
438
0
        CopyAddresses(primaryFamily, numPrimaryAddrs, results);
439
440
        // If secondary addresses are being returned, copy them into the output array after
441
        // the primary addresses.
442
0
        if (numSecondaryAddrs != 0)
443
0
        {
444
0
            CopyAddresses(secondaryFamily, numSecondaryAddrs, results);
445
0
        }
446
447
#else // INET_CONFIG_ENABLE_IPV4
448
449
        // Copy IPv6 addresses into the application's output array.
450
        CopyAddresses(AF_INET6, UINT8_MAX, results);
451
452
#endif // INET_CONFIG_ENABLE_IPV4
453
454
        // If in the end no addresses were returned, treat this as a "host not found" error.
455
0
        if (NumAddrs == 0)
456
0
        {
457
0
            err = INET_ERROR_HOST_NOT_FOUND;
458
0
        }
459
0
    }
460
461
    // Otherwise, getaddrinfo() failed, so translate the return code to an appropriate
462
    // Inet error...
463
0
    else
464
0
    {
465
0
        switch (returnCode)
466
0
        {
467
0
        case EAI_NONAME:
468
0
        case EAI_NODATA:
469
0
        case EAI_ADDRFAMILY:
470
            // Each of these errors is translated to "host not found" for simplicity at the
471
            // application layer. On most systems, the errors have the following meanings:
472
            //    EAI_NONAME is returned when there are no DNS records for the requested host name.
473
            //    EAI_NODATA is returned when there are no host records (A or AAAA) for the requested
474
            //      name, but other records do exist (e.g. MX or TXT).
475
            //    EAI_ADDRFAMILY is returned when a text-form address is given as the name, but its
476
            //      address family (IPv4 or IPv6) does not match the value specified in hints.ai_family.
477
0
            err = INET_ERROR_HOST_NOT_FOUND;
478
0
            break;
479
0
        case EAI_AGAIN:
480
0
            err = INET_ERROR_DNS_TRY_AGAIN;
481
0
            break;
482
0
        case EAI_SYSTEM:
483
0
            err = Weave::System::MapErrorPOSIX(errno);
484
0
            break;
485
0
        default:
486
0
            err = INET_ERROR_DNS_NO_RECOVERY;
487
0
            break;
488
0
        }
489
0
    }
490
491
    // Free the results structure.
492
0
    if (results != NULL)
493
0
        freeaddrinfo(results);
494
495
0
    return err;
496
0
}
497
498
void DNSResolver::CopyAddresses(int family, uint8_t count, const struct addrinfo * addrs)
499
0
{
500
0
    for (const struct addrinfo *addr = addrs;
501
0
         addr != NULL && NumAddrs < MaxAddrs && count > 0;
502
0
         addr = addr->ai_next)
503
0
    {
504
0
        if (family == AF_UNSPEC || addr->ai_addr->sa_family == family)
505
0
        {
506
0
            AddrArray[NumAddrs++] = IPAddress::FromSockAddr(*addr->ai_addr);
507
0
            count--;
508
0
        }
509
0
    }
510
0
}
511
512
uint8_t DNSResolver::CountAddresses(int family, const struct addrinfo * addrs)
513
0
{
514
0
    uint8_t count = 0;
515
516
0
    for (const struct addrinfo *addr = addrs;
517
0
         addr != NULL && count < UINT8_MAX;
518
0
         addr = addr->ai_next)
519
0
    {
520
0
        if (family == AF_UNSPEC || addr->ai_addr->sa_family == family)
521
0
        {
522
0
            count++;
523
0
        }
524
0
    }
525
526
0
    return count;
527
0
}
528
529
#if INET_CONFIG_ENABLE_ASYNC_DNS_SOCKETS
530
531
void DNSResolver::HandleAsyncResolveComplete(void)
532
0
{
533
    // Copy the resolved address to the application supplied buffer, but only if the request hasn't been canceled.
534
0
    if (OnComplete && mState != kState_Canceled)
535
0
    {
536
0
        OnComplete(AppState, asyncDNSResolveResult, NumAddrs, AddrArray);
537
0
    }
538
539
0
    Release();
540
0
}
541
#endif // INET_CONFIG_ENABLE_ASYNC_DNS_SOCKETS
542
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
543
544
} // namespace Inet
545
} // namespace nl