Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / src / inet / AsyncDNSResolverSockets.cpp
1 /*
2  *
3  *    Copyright (c) 2020 Project CHIP Authors
4  *    Copyright (c) 2017 Nest Labs, Inc.
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 AsyncDNSResolverSockets, the object that implements
22  *      Asynchronous Domain Name System (DNS) resolution in InetLayer.
23  *
24  */
25 #include <inet/InetLayer.h>
26 #include <support/CodeUtils.h>
27 #include <support/logging/CHIPLogging.h>
28
29 #include <errno.h>
30 #include <stdlib.h>
31 #include <string.h>
32
33 #if CHIP_SYSTEM_CONFIG_USE_SOCKETS
34 #if INET_CONFIG_ENABLE_DNS_RESOLVER && INET_CONFIG_ENABLE_ASYNC_DNS_SOCKETS
35
36 #include "AsyncDNSResolverSockets.h"
37
38 namespace chip {
39 namespace Inet {
40
41 /**
42  *  The explicit initializer for the AsynchronousDNSResolverSockets class.
43  *  This initializes the mutex and semaphore variables and creates the
44  *  threads for handling the asynchronous DNS resolution.
45  *
46  *  @param[in]  aInet  A pointer to the InetLayer object.
47  *
48  *  @retval #INET_NO_ERROR                   if initialization is
49  *                                           successful.
50  *  @retval other appropriate POSIX network or OS error.
51  */
52 INET_ERROR AsyncDNSResolverSockets::Init(InetLayer * aInet)
53 {
54     INET_ERROR err = INET_NO_ERROR;
55     int pthreadErr;
56
57     mInet = aInet;
58
59     mAsyncDNSQueueHead = nullptr;
60     mAsyncDNSQueueTail = nullptr;
61
62     pthreadErr = pthread_cond_init(&mAsyncDNSCondVar, nullptr);
63     VerifyOrDie(pthreadErr == 0);
64
65     pthreadErr = pthread_mutex_init(&mAsyncDNSMutex, nullptr);
66     VerifyOrDie(pthreadErr == 0);
67
68     // Create the thread pool for asynchronous DNS resolution.
69     for (int i = 0; i < INET_CONFIG_DNS_ASYNC_MAX_THREAD_COUNT; i++)
70     {
71         pthreadErr = pthread_create(&mAsyncDNSThreadHandle[i], nullptr, &AsyncDNSThreadRun, this);
72         VerifyOrDie(pthreadErr == 0);
73     }
74
75     return err;
76 }
77
78 /**
79  *  This is the explicit deinitializer of the AsyncDNSResolverSockets class
80  *  and it takes care of shutting the threads down and destroying the mutex
81  *  and semaphore variables.
82  *
83  *  @retval #INET_NO_ERROR                   if shutdown is successful.
84  *  @retval other appropriate POSIX network or OS error.
85  */
86 INET_ERROR AsyncDNSResolverSockets::Shutdown()
87 {
88     INET_ERROR err = INET_NO_ERROR;
89     int pthreadErr;
90
91     AsyncMutexLock();
92
93     mInet->State = InetLayer::kState_ShutdownInProgress;
94
95     pthreadErr = pthread_cond_broadcast(&mAsyncDNSCondVar);
96     VerifyOrDie(pthreadErr == 0);
97
98     AsyncMutexUnlock();
99
100     // Have the CHIP thread join the thread pool for asynchronous DNS resolution.
101     for (pthread_t thread : mAsyncDNSThreadHandle)
102     {
103         pthreadErr = pthread_join(thread, nullptr);
104         VerifyOrDie(pthreadErr == 0);
105     }
106
107     pthreadErr = pthread_mutex_destroy(&mAsyncDNSMutex);
108     VerifyOrDie(pthreadErr == 0);
109
110     pthreadErr = pthread_cond_destroy(&mAsyncDNSCondVar);
111     VerifyOrDie(pthreadErr == 0);
112
113     return err;
114 }
115
116 /**
117  *  This method prepares a DNSResolver object prior to asynchronous resolution.
118  *
119  *  @param[in]  resolver    A reference to an allocated DNSResolver object.
120  *
121  *  @param[in]  hostName    A pointer to a C string representing the host name
122  *                          to be queried.
123  *  @param[in]  hostNameLen The string length of host name.
124  *  @param[in]  options     An integer value controlling how host name address
125  *                          resolution is performed.  Values are from the #DNSOptions
126  *                          enumeration.
127  *  @param[in]  maxAddrs    The maximum number of addresses to store in the DNS
128  *                          table.
129  *  @param[in]  addrArray   A pointer to the DNS table.
130  *  @param[in]  onComplete  A pointer to the callback function when a DNS
131  *                          request is complete.
132  *  @param[in]  appState    A pointer to the application state to be passed to
133  *                          onComplete when a DNS request is complete.
134  *
135  *  @retval INET_NO_ERROR                   if a DNS request is handled
136  *                                          successfully.
137  *
138  */
139 INET_ERROR AsyncDNSResolverSockets::PrepareDNSResolver(DNSResolver & resolver, const char * hostName, uint16_t hostNameLen,
140                                                        uint8_t options, uint8_t maxAddrs, IPAddress * addrArray,
141                                                        DNSResolver::OnResolveCompleteFunct onComplete, void * appState)
142 {
143     INET_ERROR err = INET_NO_ERROR;
144
145     memcpy(resolver.asyncHostNameBuf, hostName, hostNameLen);
146     resolver.asyncHostNameBuf[hostNameLen] = 0;
147     resolver.MaxAddrs                      = maxAddrs;
148     resolver.NumAddrs                      = 0;
149     resolver.DNSOptions                    = options;
150     resolver.AddrArray                     = addrArray;
151     resolver.AppState                      = appState;
152     resolver.OnComplete                    = onComplete;
153     resolver.asyncDNSResolveResult         = INET_NO_ERROR;
154     resolver.mState                        = DNSResolver::kState_Active;
155     resolver.pNextAsyncDNSResolver         = nullptr;
156
157     return err;
158 }
159
160 /**
161  *  Enqueue a DNSResolver object for asynchronous IP address resolution of a specified hostname.
162  *
163  *  @param[in]  resolver    A reference to the DNSResolver object.
164  *
165  *  @retval #INET_NO_ERROR                   if a DNS request is queued
166  *                                           successfully.
167  *  @retval #INET_ERROR_NO_MEMORY            if the Inet layer resolver pool
168  *                                           is full.
169  *  @retval other appropriate POSIX network or OS error.
170  *
171  */
172 INET_ERROR AsyncDNSResolverSockets::EnqueueRequest(DNSResolver & resolver)
173 {
174     INET_ERROR err = INET_NO_ERROR;
175     int pthreadErr;
176
177     AsyncMutexLock();
178
179     // Add the DNSResolver object to the queue.
180     if (mAsyncDNSQueueHead == nullptr)
181     {
182         mAsyncDNSQueueHead = &resolver;
183     }
184
185     if (mAsyncDNSQueueTail != nullptr)
186     {
187         mAsyncDNSQueueTail->pNextAsyncDNSResolver = &resolver;
188     }
189
190     mAsyncDNSQueueTail = &resolver;
191
192     pthreadErr = pthread_cond_signal(&mAsyncDNSCondVar);
193     VerifyOrDie(pthreadErr == 0);
194
195     AsyncMutexUnlock();
196
197     return err;
198 }
199
200 /**
201  * Dequeue a DNSResolver object from the queue if there is one.
202  * Make the worker thread block if there is no item in the queue.
203  *
204  */
205 INET_ERROR AsyncDNSResolverSockets::DequeueRequest(DNSResolver ** outResolver)
206 {
207     INET_ERROR err = INET_NO_ERROR;
208     int pthreadErr;
209
210     AsyncMutexLock();
211
212     // block until there is work to do or we detect a shutdown
213     while ((mAsyncDNSQueueHead == nullptr) && (mInet->State == InetLayer::kState_Initialized))
214     {
215         pthreadErr = pthread_cond_wait(&mAsyncDNSCondVar, &mAsyncDNSMutex);
216         VerifyOrDie(pthreadErr == 0);
217     }
218
219     ChipLogDetail(Inet, "Async DNS worker thread woke up.");
220
221     // on shutdown, return NULL. Otherwise, pop the head of the DNS request queue
222     if (mInet->State != InetLayer::kState_Initialized)
223     {
224         *outResolver = nullptr;
225     }
226     else
227     {
228         *outResolver = const_cast<DNSResolver *>(mAsyncDNSQueueHead);
229
230         mAsyncDNSQueueHead = mAsyncDNSQueueHead->pNextAsyncDNSResolver;
231
232         if (mAsyncDNSQueueHead == nullptr)
233         {
234             // Queue is empty
235             mAsyncDNSQueueTail = nullptr;
236         }
237     }
238
239     AsyncMutexUnlock();
240
241     return err;
242 }
243
244 /**
245  *  Cancel an outstanding DNS query that may still be active.
246  *
247  *  @param[in]    resolver   A reference to the DNSResolver object.
248  */
249 INET_ERROR AsyncDNSResolverSockets::Cancel(DNSResolver & resolver)
250 {
251     INET_ERROR err = INET_NO_ERROR;
252
253     AsyncMutexLock();
254
255     resolver.mState = DNSResolver::kState_Canceled;
256
257     AsyncMutexUnlock();
258
259     return err;
260 }
261
262 void AsyncDNSResolverSockets::UpdateDNSResult(DNSResolver & resolver, struct addrinfo * inLookupRes)
263 {
264     resolver.NumAddrs = 0;
265
266     for (struct addrinfo * addr = inLookupRes; addr != nullptr && resolver.NumAddrs < resolver.MaxAddrs;
267          addr                   = addr->ai_next, resolver.NumAddrs++)
268     {
269         resolver.AddrArray[resolver.NumAddrs] = IPAddress::FromSockAddr(*addr->ai_addr);
270     }
271 }
272
273 void AsyncDNSResolverSockets::Resolve(DNSResolver & resolver)
274 {
275     struct addrinfo gaiHints;
276     struct addrinfo * gaiResults = nullptr;
277     int gaiReturnCode;
278
279     // Configure the hints argument for getaddrinfo()
280     resolver.InitAddrInfoHints(gaiHints);
281
282     // Call getaddrinfo() to perform the name resolution.
283     gaiReturnCode = getaddrinfo(resolver.asyncHostNameBuf, nullptr, &gaiHints, &gaiResults);
284
285     // Mutex protects the read and write operation on resolver->mState
286     AsyncMutexLock();
287
288     // Process the return code and results list returned by getaddrinfo(). If the call
289     // was successful this will copy the resultant addresses into the caller's array.
290     resolver.asyncDNSResolveResult = resolver.ProcessGetAddrInfoResult(gaiReturnCode, gaiResults);
291
292     // Set the DNS resolver state.
293     resolver.mState = DNSResolver::kState_Complete;
294
295     // Release lock.
296     AsyncMutexUnlock();
297 }
298
299 /* Event handler function for asynchronous DNS notification */
300 void AsyncDNSResolverSockets::DNSResultEventHandler(chip::System::Layer * aLayer, void * aAppState, chip::System::Error aError)
301 {
302     DNSResolver * resolver = static_cast<DNSResolver *>(aAppState);
303
304     if (resolver)
305     {
306         resolver->HandleAsyncResolveComplete();
307     }
308 }
309
310 void AsyncDNSResolverSockets::NotifyChipThread(DNSResolver * resolver)
311 {
312     // Post work item via Timer Event for the CHIP thread
313     chip::System::Layer & lSystemLayer = resolver->SystemLayer();
314
315     ChipLogDetail(Inet, "Posting DNS completion event to CHIP thread.");
316     lSystemLayer.ScheduleWork(AsyncDNSResolverSockets::DNSResultEventHandler, resolver);
317 }
318
319 void * AsyncDNSResolverSockets::AsyncDNSThreadRun(void * args)
320 {
321
322     INET_ERROR err                          = INET_NO_ERROR;
323     AsyncDNSResolverSockets * asyncResolver = static_cast<AsyncDNSResolverSockets *>(args);
324
325     while (true)
326     {
327         DNSResolver * request = nullptr;
328
329         // Dequeue a DNSResolver for resolution. This function would block until there
330         // is an item in the queue or shutdown has been called.
331         err = asyncResolver->DequeueRequest(&request);
332
333         // If shutdown has been called, DeQueue would return with an empty request.
334         // In that case, break out of the loop and exit thread.
335         VerifyOrExit(err == INET_NO_ERROR && request != nullptr, );
336
337         if (request->mState != DNSResolver::kState_Canceled)
338         {
339             asyncResolver->Resolve(*request);
340         }
341
342         asyncResolver->NotifyChipThread(request);
343     }
344
345 exit:
346     ChipLogDetail(Inet, "Async DNS worker thread exiting.");
347     return nullptr;
348 }
349
350 void AsyncDNSResolverSockets::AsyncMutexLock()
351 {
352     int pthreadErr;
353
354     pthreadErr = pthread_mutex_lock(&mAsyncDNSMutex);
355     VerifyOrDie(pthreadErr == 0);
356 }
357
358 void AsyncDNSResolverSockets::AsyncMutexUnlock()
359 {
360     int pthreadErr;
361
362     pthreadErr = pthread_mutex_unlock(&mAsyncDNSMutex);
363     VerifyOrDie(pthreadErr == 0);
364 }
365
366 } // namespace Inet
367 } // namespace chip
368 #endif // INET_CONFIG_ENABLE_DNS_RESOLVER && INET_CONFIG_ENABLE_ASYNC_DNS_SOCKETS
369 #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS