• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021-2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "nstackx_device_local.h"
17 #include <securec.h>
18 #include "nstackx_dfinder_hidump.h"
19 #include "nstackx_error.h"
20 #include "nstackx_dev.h"
21 #include "nstackx_dfinder_log.h"
22 #include "nstackx_statistics.h"
23 #include "nstackx_timer.h"
24 #include "nstackx_util.h"
25 #include "nstackx_device_remote.h"
26 #include "nstackx_list.h"
27 #include "nstackx_inet.h"
28 
29 #define TAG "LOCALDEVICE"
30 enum {
31     IFACE_STATE_READY,
32     IFACE_STATE_DESTROYING,
33     IFACE_STATE_CREATING,
34 };
35 
36 struct LocalIface {
37     List node;
38 
39     uint8_t type;
40     uint8_t state;
41     uint8_t createCount;
42     uint8_t af;
43     char ifname[NSTACKX_MAX_INTERFACE_NAME_LEN];
44     char ipStr[NSTACKX_MAX_IP_STRING_LEN];
45     union InetAddr addr;
46     char serviceData[NSTACKX_MAX_SERVICE_DATA_LEN];
47     struct timespec updateTime;
48 
49     Timer *timer;
50     CoapCtxType *ctx;
51 };
52 
53 typedef struct LocalDevice_ {
54     DeviceInfo deviceInfo;
55 
56     List readyList[IFACE_TYPE_MAX];
57     List creatingList;
58     List destroyList;
59 
60     Timer *timer;
61     bool inited;
62 } LocalDevice;
63 
64 static LocalDevice g_localDevice;
65 
66 #define LOCAL_DEVICE_OFFLINE_DEFERRED_DURATION 5000 /* Defer local device offline event, 5 seconds */
67 
68 #define NSTACKX_DEFAULT_DEVICE_NAME "nStack Device"
69 #define NSTACKX_IPV6_MULTICAST_ADDR "FF02::1"
70 #define DEFAULT_IFACE_NUM 1
71 
72 #define IFACE_COAP_CTX_INIT_MAX_RETRY_TIMES 4
73 static const uint32_t g_ifaceCoapCtxRetryBackoffList[IFACE_COAP_CTX_INIT_MAX_RETRY_TIMES] = { 10, 15, 25, 100 };
74 
75 static pthread_mutex_t g_capabilityLock = PTHREAD_MUTEX_INITIALIZER;
76 static pthread_mutex_t g_serviceDataLock = PTHREAD_MUTEX_INITIALIZER;
77 static pthread_mutex_t g_businessDataLock = PTHREAD_MUTEX_INITIALIZER;
78 static pthread_mutex_t g_extendServiceDataLock = PTHREAD_MUTEX_INITIALIZER;
79 static pthread_mutex_t g_deviceInfoLock = PTHREAD_MUTEX_INITIALIZER;
80 
LocalDeviceTimeout(void * data)81 static void LocalDeviceTimeout(void *data)
82 {
83     (void)data;
84 
85     struct timespec cur;
86     ClockGetTime(CLOCK_MONOTONIC, &cur);
87 
88     uint32_t nextTimeout = 0;
89     List *pos = NULL;
90     List *tmp = NULL;
91     LIST_FOR_EACH_SAFE(pos, tmp, &g_localDevice.destroyList) {
92         struct LocalIface *iface = (struct LocalIface *)pos;
93         uint32_t diff = GetTimeDiffMs(&cur, &iface->updateTime);
94         if (diff < LOCAL_DEVICE_OFFLINE_DEFERRED_DURATION) {
95             nextTimeout = LOCAL_DEVICE_OFFLINE_DEFERRED_DURATION - diff;
96             break;
97         }
98 
99         DestroyLocalIface(iface, NSTACKX_FALSE);
100     }
101 
102     if (nextTimeout != 0) {
103         DFINDER_LOGD(TAG, "start offline timer again, timeout %u", nextTimeout);
104         (void)TimerSetTimeout(g_localDevice.timer, nextTimeout, NSTACKX_FALSE);
105     }
106 }
107 
LocalDeviceDeinit(void)108 void LocalDeviceDeinit(void)
109 {
110     if (!g_localDevice.inited) {
111         DFINDER_LOGW(TAG, "local device not inited");
112         return;
113     }
114 
115     List *pos = NULL;
116     List *tmp = NULL;
117     int i;
118     for (i = 0; i < IFACE_TYPE_MAX; i++) {
119         LIST_FOR_EACH_SAFE(pos, tmp, &g_localDevice.readyList[i]) {
120             DestroyLocalIface((struct LocalIface *)pos, NSTACKX_TRUE);
121         }
122     }
123 
124     LIST_FOR_EACH_SAFE(pos, tmp, &g_localDevice.destroyList) {
125         DestroyLocalIface((struct LocalIface *)pos, NSTACKX_TRUE);
126     }
127 
128     LIST_FOR_EACH_SAFE(pos, tmp, &g_localDevice.creatingList) {
129         DestroyLocalIface((struct LocalIface *)pos, NSTACKX_TRUE);
130     }
131 
132     if (g_localDevice.timer != NULL) {
133         TimerDelete(g_localDevice.timer);
134         g_localDevice.timer = NULL;
135     }
136 
137     g_localDevice.inited = NSTACKX_FALSE;
138 }
139 
LocalDeviceInit(EpollDesc epollfd)140 int LocalDeviceInit(EpollDesc epollfd)
141 {
142     (void)memset_s(&g_localDevice, sizeof(g_localDevice), 0, sizeof(g_localDevice));
143     g_localDevice.timer = TimerStart(epollfd, 0, NSTACKX_FALSE, LocalDeviceTimeout, NULL);
144     if (g_localDevice.timer == NULL) {
145         DFINDER_LOGE(TAG, "timer init failed");
146         return NSTACKX_EFAILED;
147     }
148 
149     int i;
150     for (i = 0; i < IFACE_TYPE_MAX; i++) {
151         ListInitHead(&g_localDevice.readyList[i]);
152     }
153 
154     ListInitHead(&g_localDevice.destroyList);
155     ListInitHead(&g_localDevice.creatingList);
156     g_localDevice.inited = NSTACKX_TRUE;
157 
158     return NSTACKX_EOK;
159 }
160 
ResetLocalDeviceTaskCount(uint8_t isBusy)161 void ResetLocalDeviceTaskCount(uint8_t isBusy)
162 {
163     if (g_localDevice.timer != NULL) {
164         if (isBusy) {
165             DFINDER_LOGI(TAG, "in this busy interval: offline deferred timer task count %llu",
166                          g_localDevice.timer->task.count);
167         }
168         g_localDevice.timer->task.count = 0;
169     }
170 }
171 
LocalIfaceChangeState(struct LocalIface * iface,List * targetList,uint8_t state)172 static inline void LocalIfaceChangeState(struct LocalIface *iface, List *targetList, uint8_t state)
173 {
174     DFINDER_LOGI(TAG, "iface %s state change: %hhu -> %hhu", iface->ifname, iface->state, state);
175     ListRemoveNode(&iface->node);
176     iface->state = state;
177     ListInsertTail(targetList, &iface->node);
178 }
179 
LocalIfaceCreateContextTimeout(void * arg)180 static void LocalIfaceCreateContextTimeout(void *arg)
181 {
182     struct LocalIface *iface = (struct LocalIface *)arg;
183     DFINDER_LOGD(TAG, "iface %s create context for %u times", iface->ifname, iface->createCount);
184     iface->ctx = CoapServerInit(iface->af, &iface->addr, (void *)iface);
185     if (iface->ctx != NULL) {
186         DFINDER_LOGD(TAG, "iface %s create coap context success", iface->ifname);
187         TimerDelete(iface->timer);
188         iface->timer = NULL;
189         LocalIfaceChangeState(iface, &g_localDevice.readyList[iface->type], IFACE_STATE_READY);
190         return;
191     }
192 
193     if (iface->createCount >= IFACE_COAP_CTX_INIT_MAX_RETRY_TIMES) {
194         DFINDER_LOGE(TAG, "create context retry reach max times %hhu", iface->createCount);
195         DestroyLocalIface(iface, NSTACKX_FALSE);
196         return;
197     }
198 
199     (void)TimerSetTimeout(iface->timer, g_ifaceCoapCtxRetryBackoffList[iface->createCount], NSTACKX_FALSE);
200     iface->createCount++;
201 }
202 
NeedCreateSynchronously(uint8_t ifaceType)203 static inline bool NeedCreateSynchronously(uint8_t ifaceType)
204 {
205     return ifaceType < IFACE_TYPE_P2P;
206 }
207 
LocalIfaceInit(struct LocalIface * iface,const char * ifname,const char * ipStr)208 static int LocalIfaceInit(struct LocalIface *iface, const char *ifname, const char *ipStr)
209 {
210     DFINDER_LOGI(TAG, "trying to bring up interface %s", ifname);
211 
212     if (strcpy_s(iface->ifname, sizeof(iface->ifname), ifname) != EOK) {
213         DFINDER_LOGE(TAG, "copy ifname %s failed", ifname);
214         return NSTACKX_EFAILED;
215     }
216     if (strcpy_s(iface->ipStr, sizeof(iface->ipStr), ipStr) != EOK) {
217         DFINDER_LOGE(TAG, "copy iface %s ip string failed", ifname);
218         return NSTACKX_EFAILED;
219     }
220 
221     if (NeedCreateSynchronously(iface->type)) {
222         iface->ctx = CoapServerInit(iface->af, &iface->addr, (void *)iface);
223         if (iface->ctx == NULL) {
224             DFINDER_LOGE(TAG, "create coap context for iface %s failed", ifname);
225             IncStatistics(STATS_CREATE_SERVER_FAILED);
226             return NSTACKX_EFAILED;
227         }
228         iface->state = IFACE_STATE_READY;
229         ListInsertTail(&g_localDevice.readyList[iface->type], &iface->node);
230         DFINDER_LOGI(TAG, "bring up interface %s success", ifname);
231     } else {
232         iface->timer = TimerStart(GetEpollFD(), g_ifaceCoapCtxRetryBackoffList[0],
233             NSTACKX_FALSE, LocalIfaceCreateContextTimeout, iface);
234         if (iface->timer == NULL) {
235             DFINDER_LOGE(TAG, "iface %s create timer to create context async failed", iface->ifname);
236             return NSTACKX_EFAILED;
237         }
238         iface->createCount = 1;
239         iface->state = IFACE_STATE_CREATING;
240         ListInsertTail(&g_localDevice.creatingList, &iface->node);
241     }
242 
243     return NSTACKX_EOK;
244 }
245 
CreateLocalIface(const char * ifname,const char * serviceData,uint8_t af,const union InetAddr * addr,const char * ipStr)246 static struct LocalIface *CreateLocalIface(const char *ifname, const char *serviceData, uint8_t af,
247     const union InetAddr *addr, const char *ipStr)
248 {
249     struct LocalIface *iface = (struct LocalIface *)calloc(1, sizeof(struct LocalIface));
250     if (iface == NULL) {
251         DFINDER_LOGE(TAG, "calloc local iface fail");
252         return NULL;
253     }
254 
255     iface->type = GetIfaceType(ifname);
256     DFINDER_LOGI(TAG, "GetIfaceType type %hhu", iface->type);
257     iface->addr = *addr;
258     iface->af = af;
259     (void)memcpy_s(iface->serviceData, NSTACKX_MAX_SERVICE_DATA_LEN, serviceData, NSTACKX_MAX_SERVICE_DATA_LEN);
260     if (LocalIfaceInit(iface, ifname, ipStr) != NSTACKX_EOK) {
261         DFINDER_LOGE(TAG, "local iface %s init failed", ifname);
262         free(iface);
263         return NULL;
264     }
265 
266     return iface;
267 }
268 
DestroyLocalIface(struct LocalIface * iface,bool moduleDeinit)269 void DestroyLocalIface(struct LocalIface *iface, bool moduleDeinit)
270 {
271     DFINDER_LOGI(TAG, "destroy iface %s, type: %hhu state: %hhu", iface->ifname, iface->type, iface->state);
272 
273 #ifdef DFINDER_SAVE_DEVICE_LIST
274     DestroyRxIfaceByIfname(iface->ifname);
275 #endif
276 
277     if (iface->ctx != NULL) {
278         CoapServerDestroy(iface->ctx, moduleDeinit);
279     }
280     ListRemoveNode(&iface->node);
281     if (iface->timer != NULL) {
282         TimerDelete(iface->timer);
283     }
284     free(iface);
285 }
286 
GetLocalIface(List * head,const char * ifname,uint8_t af,const union InetAddr * addr)287 static struct LocalIface *GetLocalIface(List *head, const char *ifname, uint8_t af, const union InetAddr *addr)
288 {
289     List *pos = NULL;
290     LIST_FOR_EACH(pos, head) {
291         struct LocalIface *iface = (struct LocalIface *)pos;
292         if (iface->af != af || strcmp(iface->ifname, ifname) != 0) {
293             continue;
294         }
295         if (addr == NULL || InetEqual(af, addr, &iface->addr)) {
296             return iface;
297         }
298     }
299 
300     return NULL;
301 }
302 
GetActiveLocalIface(uint8_t af,const char * ifname)303 static struct LocalIface *GetActiveLocalIface(uint8_t af, const char *ifname)
304 {
305     uint8_t type = GetIfaceType(ifname);
306     struct LocalIface *iface = GetLocalIface(&g_localDevice.readyList[type], ifname, af, NULL);
307     if (iface == NULL) {
308         iface = GetLocalIface(&g_localDevice.creatingList, ifname, af, NULL);
309     }
310 
311     return iface;
312 }
313 
AddToDestroyList(struct LocalIface * iface)314 static void AddToDestroyList(struct LocalIface *iface)
315 {
316     if (iface->state != IFACE_STATE_DESTROYING) {
317         if (ListIsEmpty(&g_localDevice.destroyList)) {
318             (void)TimerSetTimeout(g_localDevice.timer, LOCAL_DEVICE_OFFLINE_DEFERRED_DURATION, NSTACKX_FALSE);
319             DFINDER_LOGD(TAG, "iface %s start offline timer", iface->ifname);
320         }
321         LocalIfaceChangeState(iface, &g_localDevice.destroyList, IFACE_STATE_DESTROYING);
322         (void)memset_s(iface->serviceData, NSTACKX_MAX_SERVICE_DATA_LEN, 0, NSTACKX_MAX_SERVICE_DATA_LEN);
323         ClockGetTime(CLOCK_MONOTONIC, &iface->updateTime);
324         if (iface->timer != NULL) {
325             (void)TimerSetTimeout(iface->timer, 0, NSTACKX_FALSE);
326             iface->createCount = 0;
327         }
328     }
329 }
330 
AddLocalIface(const char * ifname,const char * serviceData,uint8_t af,const union InetAddr * addr)331 int AddLocalIface(const char *ifname, const char *serviceData, uint8_t af, const union InetAddr *addr)
332 {
333     struct LocalIface *iface = GetActiveLocalIface(af, ifname);
334     if (iface == NULL) {
335         iface = GetLocalIface(&g_localDevice.destroyList, ifname, af, addr);
336         if (iface != NULL) {
337             DFINDER_LOGI(TAG, "iface %s is in destroying, now make it in ready state", ifname);
338             (void)memcpy_s(iface->serviceData, NSTACKX_MAX_SERVICE_DATA_LEN,
339                 serviceData, NSTACKX_MAX_SERVICE_DATA_LEN);
340             LocalIfaceChangeState(iface, &g_localDevice.readyList[iface->type], IFACE_STATE_READY);
341             return NSTACKX_EOK;
342         }
343     } else {
344         if (iface->af == af && InetEqual(af, &(iface->addr), addr)) {
345             DFINDER_LOGI(TAG, "iface %s already existed and in correct state", ifname);
346             (void)memcpy_s(iface->serviceData, NSTACKX_MAX_SERVICE_DATA_LEN,
347                 serviceData, NSTACKX_MAX_SERVICE_DATA_LEN);
348             return NSTACKX_EOK;
349         }
350         AddToDestroyList(iface);
351     }
352 
353     char ipStr[NSTACKX_MAX_IP_STRING_LEN] = {0};
354     if (inet_ntop(af, addr, ipStr, sizeof(ipStr)) == NULL) {
355         DFINDER_LOGE(TAG, "inet_ntop fail, errno: %d, desc: %s", errno, strerror(errno));
356         return NSTACKX_EFAILED;
357     }
358     iface = CreateLocalIface(ifname, serviceData, af, addr, ipStr);
359     return (iface == NULL) ? NSTACKX_EFAILED : NSTACKX_EOK;
360 }
361 
RemoveLocalIface(uint8_t af,const char * ifname)362 void RemoveLocalIface(uint8_t af, const char *ifname)
363 {
364     struct LocalIface *iface = GetActiveLocalIface(af, ifname);
365     if (iface == NULL) {
366         DFINDER_LOGI(TAG, "af %hhu iface %s not found when deleting iface", af, ifname);
367         return;
368     }
369 
370     AddToDestroyList(iface);
371 }
372 
RemoveAllLocalIfaceOfList(List * list)373 static void RemoveAllLocalIfaceOfList(List *list)
374 {
375     List *pos = NULL;
376     List *tmp = NULL;
377     LIST_FOR_EACH_SAFE(pos, tmp, list) {
378         AddToDestroyList((struct LocalIface *)pos);
379     }
380 }
381 
RemoveAllLocalIface(void)382 static void RemoveAllLocalIface(void)
383 {
384     for (uint8_t i = IFACE_TYPE_ETH; i < IFACE_TYPE_MAX; i++) {
385         RemoveAllLocalIfaceOfList(&g_localDevice.readyList[i]);
386     }
387 
388     RemoveAllLocalIfaceOfList(&g_localDevice.creatingList);
389 }
390 
DestroyAllLocalIfaceOfList(List * list,uint8_t af)391 static void DestroyAllLocalIfaceOfList(List *list, uint8_t af)
392 {
393     List *pos = NULL;
394     List *tmp = NULL;
395     LIST_FOR_EACH_SAFE(pos, tmp, list) {
396         struct LocalIface *iface = (struct LocalIface *)pos;
397         if (iface->af != af) {
398             continue;
399         }
400         DestroyLocalIface(iface, true);
401     }
402 }
403 
DestroyAllLocalIface(uint8_t af)404 static void DestroyAllLocalIface(uint8_t af)
405 {
406     for (uint8_t i = IFACE_TYPE_ETH; i < IFACE_TYPE_MAX; i++) {
407         DestroyAllLocalIfaceOfList(&g_localDevice.readyList[i], af);
408     }
409 
410     DestroyAllLocalIfaceOfList(&g_localDevice.creatingList, af);
411     DestroyAllLocalIfaceOfList(&g_localDevice.destroyList, af);
412 }
413 
IsDestroyIfaceByAddr(const NSTACKX_InterfaceInfo * ifInfo,uint32_t ifNums)414 static bool IsDestroyIfaceByAddr(const NSTACKX_InterfaceInfo *ifInfo, uint32_t ifNums)
415 {
416     if (ifNums != DEFAULT_IFACE_NUM) {
417         DFINDER_LOGW(TAG, "ifnums is %u not match, so no need destroy iface", ifNums);
418         return false;
419     }
420     union InetAddr addr;
421     uint8_t af = InetGetAfType(ifInfo[0].networkIpAddr, &addr);
422     if (af == AF_ERROR) {
423         DFINDER_LOGE(TAG, "inet_pton fail");
424         return false;
425     }
426     if (InetEqualZero(af, &addr) || InetEqualLoop(af, ifInfo[0].networkIpAddr)) {
427         DFINDER_LOGI(TAG, "destroy ip with any with af %hhu th nic %s", af, ifInfo[0].networkName);
428         DestroyAllLocalIface(af);
429         return true;
430     }
431     return false;
432 }
433 
RemoveSpecifiedLocalIface(const NSTACKX_InterfaceInfo * ifInfo,uint32_t ifNums)434 static void RemoveSpecifiedLocalIface(const NSTACKX_InterfaceInfo *ifInfo, uint32_t ifNums)
435 {
436     uint32_t i;
437     for (i = 0; i < ifNums; ++i) {
438         // af is always AF_INET/AF_INET6
439         union InetAddr addr;
440         uint8_t af = InetGetAfType(ifInfo[i].networkIpAddr, &addr);
441         struct LocalIface *iface = GetActiveLocalIface(af, ifInfo[i].networkName);
442         if (iface == NULL) {
443             DFINDER_LOGI(TAG, "af %hhu iface %s not found when deleting iface", af, ifInfo[i].networkName);
444             continue;
445         }
446 
447         if (af == AF_INET) {
448             AddToDestroyList(iface);
449         } else {
450             // USB(ipv6) use old iface, send coap unicast failed(network unreachable), must destroy iface
451             DestroyLocalIface(iface, true);
452         }
453     }
454 }
455 
AddLocalIfaceIpChanged(const NSTACKX_InterfaceInfo * ifInfo,uint32_t ifNums)456 static int AddLocalIfaceIpChanged(const NSTACKX_InterfaceInfo *ifInfo, uint32_t ifNums)
457 {
458     uint8_t ifaceType = IFACE_TYPE_MAX;
459     uint32_t i;
460     for (i = 0; i < ifNums; ++i) {
461         if (ifInfo[i].networkName[0] == '\0' || ifInfo[i].networkIpAddr[0] == '\0') {
462             DFINDER_LOGI(TAG, "skip empty network name or ip addr");
463             continue;
464         }
465 
466         union InetAddr addr;
467         uint8_t af = InetGetAfType(ifInfo[i].networkIpAddr, &addr);
468         if (af == AF_ERROR) {
469             DFINDER_LOGE(TAG, "inet_pton fail for %u th nic %s, errno: %d, desc: %s",
470                 i, ifInfo[i].networkName, errno, strerror(errno));
471             return NSTACKX_EFAILED;
472         }
473         if (InetEqualZero(af, &addr) || InetEqualLoop(af, ifInfo[i].networkIpAddr)) {
474             DFINDER_LOGI(TAG, "skip ip with any with %u af %hhu th nic %s", i, af, ifInfo[i].networkName);
475             continue;
476         }
477 
478         if (AddLocalIface(ifInfo[i].networkName, ifInfo[i].serviceData, af, &addr) != NSTACKX_EOK) {
479             DFINDER_LOGE(TAG, "create local iface %s failed", ifInfo[i].networkName);
480             return NSTACKX_EFAILED;
481         }
482 
483         uint8_t curIfaceType = GetIfaceType(ifInfo[i].networkName);
484         if (curIfaceType < ifaceType) { /* storge the highest priority interface name */
485             if (strcpy_s(g_localDevice.deviceInfo.networkName, sizeof(g_localDevice.deviceInfo.networkName),
486                 ifInfo[i].networkName) != EOK) {
487                 DFINDER_LOGE(TAG, "copy ifname %s failed", ifInfo[i].networkName);
488                 return NSTACKX_EFAILED;
489             }
490             ifaceType = curIfaceType;
491         }
492     }
493 
494     return NSTACKX_EOK;
495 }
496 
CopyDeviceInfoV2(const NSTACKX_LocalDeviceInfoV2 * devInfo)497 static int CopyDeviceInfoV2(const NSTACKX_LocalDeviceInfoV2 *devInfo)
498 {
499     if (strcpy_s(g_localDevice.deviceInfo.deviceId, sizeof(g_localDevice.deviceInfo.deviceId),
500         devInfo->deviceId) != EOK) {
501         DFINDER_LOGE(TAG, "copy device id failed");
502         return NSTACKX_EFAILED;
503     }
504 
505     if (devInfo->name[0] == '\0') {
506         DFINDER_LOGW(TAG, "try to register invalid device name, will use default: %s", NSTACKX_DEFAULT_DEVICE_NAME);
507         (void)strcpy_s(g_localDevice.deviceInfo.deviceName,
508             sizeof(g_localDevice.deviceInfo.deviceName), NSTACKX_DEFAULT_DEVICE_NAME);
509     } else {
510         if (strcpy_s(g_localDevice.deviceInfo.deviceName,
511             sizeof(g_localDevice.deviceInfo.deviceName), devInfo->name) != EOK) {
512             DFINDER_LOGE(TAG, "copy device name %s failed", devInfo->name);
513             return NSTACKX_EFAILED;
514         }
515     }
516 
517     g_localDevice.deviceInfo.deviceType = devInfo->deviceType;
518     g_localDevice.deviceInfo.businessType = devInfo->businessType;
519     if (devInfo->hasDeviceHash) {
520         SetLocalDeviceHash(devInfo->deviceHash);
521     }
522 
523     return NSTACKX_EOK;
524 }
525 
RegisterLocalDeviceV2(const NSTACKX_LocalDeviceInfoV2 * devInfo,int registerType)526 int RegisterLocalDeviceV2(const NSTACKX_LocalDeviceInfoV2 *devInfo, int registerType)
527 {
528     if (PthreadMutexLock(&g_deviceInfoLock) != 0) {
529         DFINDER_LOGE(TAG, "failed to lock");
530         return NSTACKX_EFAILED;
531     }
532     if (IsDestroyIfaceByAddr(devInfo->localIfInfo, devInfo->ifNums)) {
533         if (PthreadMutexUnlock(&g_deviceInfoLock) != 0) {
534             DFINDER_LOGE(TAG, "failed to unlock");
535         }
536         return NSTACKX_EOK;
537     }
538     if (registerType == REGISTER_TYPE_UPDATE_ALL) {
539         RemoveAllLocalIface();
540     } else {
541         RemoveSpecifiedLocalIface(devInfo->localIfInfo, devInfo->ifNums);
542     }
543 
544     if (CopyDeviceInfoV2(devInfo) != NSTACKX_EOK) {
545         if (PthreadMutexUnlock(&g_deviceInfoLock) != 0) {
546             DFINDER_LOGE(TAG, "failed to unlock");
547         }
548         return NSTACKX_EFAILED;
549     }
550 
551     if (AddLocalIfaceIpChanged(devInfo->localIfInfo, devInfo->ifNums) != NSTACKX_EOK) {
552         if (registerType == REGISTER_TYPE_UPDATE_ALL) {
553             RemoveAllLocalIface(); /* maybe some ifaces is added, so remove all ifaces again */
554         }
555         if (PthreadMutexUnlock(&g_deviceInfoLock) != 0) {
556             DFINDER_LOGE(TAG, "failed to unlock");
557         }
558         return NSTACKX_EFAILED;
559     }
560     if (PthreadMutexUnlock(&g_deviceInfoLock) != 0) {
561         DFINDER_LOGE(TAG, "failed to unlock");
562         return NSTACKX_EFAILED;
563     }
564 
565     return NSTACKX_EOK;
566 }
567 
ConfigureLocalDeviceName(const char * localDeviceName)568 void ConfigureLocalDeviceName(const char *localDeviceName)
569 {
570     char backupDevName[NSTACKX_MAX_DEVICE_NAME_LEN] = {0};
571     if (memcpy_s(backupDevName, sizeof(backupDevName), g_localDevice.deviceInfo.deviceName,
572         sizeof(g_localDevice.deviceInfo.deviceName)) != EOK) {
573         DFINDER_LOGE(TAG, "backup local device name failed!");
574         return;
575     }
576     if (strncpy_s(g_localDevice.deviceInfo.deviceName, NSTACKX_MAX_DEVICE_NAME_LEN,
577         localDeviceName, NSTACKX_MAX_DEVICE_NAME_LEN - 1) != EOK) {
578         DFINDER_LOGW(TAG, "copy local device failed, will use current name");
579         if (strcpy_s(g_localDevice.deviceInfo.deviceName, NSTACKX_MAX_DEVICE_NAME_LEN, backupDevName) != EOK) {
580             DFINDER_LOGE(TAG, "config device name failed and cannot restore!");
581         }
582     }
583 }
584 
SetLocalDeviceHash(uint64_t deviceHash)585 void SetLocalDeviceHash(uint64_t deviceHash)
586 {
587     (void)memset_s(g_localDevice.deviceInfo.deviceHash, sizeof(g_localDevice.deviceInfo.deviceHash),
588         0, sizeof(g_localDevice.deviceInfo.deviceHash));
589     if (sprintf_s(g_localDevice.deviceInfo.deviceHash, DEVICE_HASH_LEN,
590         "%ju", deviceHash) == -1) {
591         DFINDER_LOGE(TAG, "set device hash error");
592     }
593 }
594 
SetLocalDeviceCapability(uint32_t capabilityBitmapNum,uint32_t capabilityBitmap[])595 int SetLocalDeviceCapability(uint32_t capabilityBitmapNum, uint32_t capabilityBitmap[])
596 {
597     if (PthreadMutexLock(&g_capabilityLock) != 0) {
598         DFINDER_LOGE(TAG, "failed to lock");
599         return NSTACKX_EFAILED;
600     }
601     (void)memset_s(g_localDevice.deviceInfo.capabilityBitmap, sizeof(g_localDevice.deviceInfo.capabilityBitmap),
602         0, sizeof(g_localDevice.deviceInfo.capabilityBitmap));
603     g_localDevice.deviceInfo.capabilityBitmapNum = 0;
604 
605     if (capabilityBitmapNum > 0) {
606         if (memcpy_s(g_localDevice.deviceInfo.capabilityBitmap, sizeof(g_localDevice.deviceInfo.capabilityBitmap),
607             capabilityBitmap, sizeof(uint32_t) * capabilityBitmapNum) != EOK) {
608             DFINDER_LOGE(TAG, "capabilityBitmap copy error");
609             if (PthreadMutexUnlock(&g_capabilityLock) != 0) {
610                 DFINDER_LOGE(TAG, "failed to unlock");
611             }
612             return NSTACKX_EFAILED;
613         }
614     }
615 
616     g_localDevice.deviceInfo.capabilityBitmapNum = capabilityBitmapNum;
617     if (PthreadMutexUnlock(&g_capabilityLock) != 0) {
618         DFINDER_LOGE(TAG, "failed to unlock");
619         return NSTACKX_EFAILED;
620     }
621     return NSTACKX_EOK;
622 }
623 
SetLocalDeviceServiceData(const char * serviceData)624 int32_t SetLocalDeviceServiceData(const char *serviceData)
625 {
626     if (PthreadMutexLock(&g_serviceDataLock) != 0) {
627         DFINDER_LOGE(TAG, "failed to lock");
628         return NSTACKX_EFAILED;
629     }
630     if (strcpy_s(g_localDevice.deviceInfo.serviceData, NSTACKX_MAX_SERVICE_DATA_LEN, serviceData) != EOK) {
631         DFINDER_LOGE(TAG, "serviceData copy error");
632         if (PthreadMutexUnlock(&g_serviceDataLock) != 0) {
633             DFINDER_LOGE(TAG, "failed to unlock");
634         }
635         return NSTACKX_EFAILED;
636     }
637     if (PthreadMutexUnlock(&g_serviceDataLock) != 0) {
638         DFINDER_LOGE(TAG, "failed to unlock");
639         return NSTACKX_EFAILED;
640     }
641     return NSTACKX_EOK;
642 }
643 
GetLocalIfaceByIp(List * head,uint8_t af,const char * ip,uint32_t len)644 static struct LocalIface *GetLocalIfaceByIp(List *head, uint8_t af, const char *ip, uint32_t len)
645 {
646     List *pos = NULL;
647     LIST_FOR_EACH(pos, head) {
648         struct LocalIface *iface = (struct LocalIface *)pos;
649         if (iface->af != af || len != strlen(iface->ipStr)) {
650             continue;
651         }
652 
653         if (strcmp(iface->ipStr, ip) == 0) {
654             return iface;
655         }
656     }
657 
658     return NULL;
659 }
660 
UpdateLocalDeviceServiceDataV2(uint8_t af,const struct NSTACKX_ServiceData * data)661 static int32_t UpdateLocalDeviceServiceDataV2(uint8_t af, const struct NSTACKX_ServiceData *data)
662 {
663     uint8_t i;
664     struct LocalIface *iface = NULL;
665     const char *ip = data->ip;
666     uint32_t len = (uint32_t)strlen(data->ip);
667 
668     iface = GetLocalIfaceByIp(&g_localDevice.creatingList, af, ip, len);
669     if (iface != NULL) {
670         (void)memcpy_s(iface->serviceData, NSTACKX_MAX_SERVICE_DATA_LEN,
671                        data->serviceData, NSTACKX_MAX_SERVICE_DATA_LEN);
672         DFINDER_LOGI(TAG, "set service data sucess");
673         return NSTACKX_EOK;
674     }
675 
676     for (i = 0; i < IFACE_TYPE_MAX; i++) {
677         iface = GetLocalIfaceByIp(&g_localDevice.readyList[i], af, ip, len);
678         if (iface != NULL) {
679             (void)memcpy_s(iface->serviceData, NSTACKX_MAX_SERVICE_DATA_LEN,
680                            data->serviceData, NSTACKX_MAX_SERVICE_DATA_LEN);
681             DFINDER_LOGI(TAG, "set service data sucess");
682             return NSTACKX_EOK;
683         }
684     }
685 
686     return NSTACKX_EFAILED;
687 }
688 
SetLocalDeviceServiceDataV2(const struct NSTACKX_ServiceData * param,uint32_t cnt)689 int32_t SetLocalDeviceServiceDataV2(const struct NSTACKX_ServiceData *param, uint32_t cnt)
690 {
691     int32_t ret = NSTACKX_EFAILED;
692     uint32_t i;
693     if (PthreadMutexLock(&g_serviceDataLock) != 0) {
694         DFINDER_LOGE(TAG, "failed to lock");
695         return NSTACKX_EFAILED;
696     }
697 
698     for (i = 0; i < cnt; i++) {
699         union InetAddr addr;
700         uint8_t af = InetGetAfType(param[i].ip, &addr);
701         if (UpdateLocalDeviceServiceDataV2(af, &(param[i])) == NSTACKX_EOK) {
702             DFINDER_LOGI(TAG, "update local device serviceData by param[%u]", i);
703             ret = NSTACKX_EOK;
704         }
705     }
706     if (PthreadMutexUnlock(&g_serviceDataLock) != 0) {
707         DFINDER_LOGE(TAG, "failed to unlock");
708         return NSTACKX_EFAILED;
709     }
710     return ret;
711 }
712 
SetLocalDeviceBusinessType(uint8_t businessType)713 void SetLocalDeviceBusinessType(uint8_t businessType)
714 {
715     g_localDevice.deviceInfo.businessType = businessType;
716 }
717 
GetLocalDeviceBusinessType(void)718 uint8_t GetLocalDeviceBusinessType(void)
719 {
720     return g_localDevice.deviceInfo.businessType;
721 }
722 
SetLocalDeviceBusinessData(const char * data,bool unicast)723 int SetLocalDeviceBusinessData(const char *data, bool unicast)
724 {
725     if (PthreadMutexLock(&g_businessDataLock) != 0) {
726         DFINDER_LOGE(TAG, "failed to lock");
727         return NSTACKX_EFAILED;
728     }
729     int ret = EOK;
730     if (unicast) {
731         ret = strcpy_s(g_localDevice.deviceInfo.businessData.businessDataUnicast,
732             NSTACKX_MAX_BUSINESS_DATA_LEN, data);
733     } else {
734         ret = strcpy_s(g_localDevice.deviceInfo.businessData.businessDataBroadcast,
735             NSTACKX_MAX_BUSINESS_DATA_LEN, data);
736     }
737 
738     if (ret != EOK) {
739         DFINDER_LOGE(TAG, "businessData copy error, unicast: %d", unicast);
740         if (PthreadMutexUnlock(&g_businessDataLock) != 0) {
741             DFINDER_LOGE(TAG, "failed to unlock");
742         }
743         return NSTACKX_EFAILED;
744     }
745 
746     if (PthreadMutexUnlock(&g_businessDataLock) != 0) {
747         DFINDER_LOGE(TAG, "failed to unlock");
748         return NSTACKX_EFAILED;
749     }
750 
751     return NSTACKX_EOK;
752 }
753 
LocalizeNotificationMsg(const char * msg)754 int32_t LocalizeNotificationMsg(const char *msg)
755 {
756     if (strcpy_s(g_localDevice.deviceInfo.notification, NSTACKX_MAX_NOTIFICATION_DATA_LEN, msg) != EOK) {
757         DFINDER_LOGE(TAG, "copy notification msg to local dev failed");
758         return NSTACKX_EFAILED;
759     }
760     return NSTACKX_EOK;
761 }
762 
GetLocalDeviceMode(void)763 uint8_t GetLocalDeviceMode(void)
764 {
765     return g_localDevice.deviceInfo.mode;
766 }
767 
SetLocalDeviceMode(uint8_t mode)768 void SetLocalDeviceMode(uint8_t mode)
769 {
770     g_localDevice.deviceInfo.mode = mode;
771 }
772 
773 #ifndef DFINDER_USE_MINI_NSTACKX
SetLocalDeviceExtendServiceData(const char * extendServiceData)774 int32_t SetLocalDeviceExtendServiceData(const char *extendServiceData)
775 {
776     if (PthreadMutexLock(&g_extendServiceDataLock) != 0) {
777         DFINDER_LOGE(TAG, "failed to lock");
778         return NSTACKX_EFAILED;
779     }
780     if (strcpy_s(g_localDevice.deviceInfo.extendServiceData, NSTACKX_MAX_EXTEND_SERVICE_DATA_LEN,
781         extendServiceData) != EOK) {
782         DFINDER_LOGE(TAG, "extendServiceData copy error");
783         if (PthreadMutexUnlock(&g_extendServiceDataLock) != 0) {
784             DFINDER_LOGE(TAG, "failed to unlock");
785         }
786         return NSTACKX_EFAILED;
787     }
788     if (PthreadMutexUnlock(&g_extendServiceDataLock) != 0) {
789         DFINDER_LOGE(TAG, "failed to unlock");
790         return NSTACKX_EFAILED;
791     }
792     return NSTACKX_EOK;
793 }
794 
795 #ifndef _WIN32
DetectLocalIface(void * arg)796 void DetectLocalIface(void *arg)
797 {
798     struct ifconf ifc;
799     struct ifreq req[INTERFACE_MAX];
800     int fd = GetInterfaceList(&ifc, req, sizeof(req));
801     if (fd < 0) {
802         DFINDER_LOGE(TAG, "get iface list failed");
803         return;
804     }
805 
806     int interfaceNum = ifc.ifc_len / (int)sizeof(struct ifreq);
807     for (int i = 0; i < interfaceNum && i < INTERFACE_MAX; i++) {
808         /* get IP of this interface */
809         int state = GetInterfaceIP(fd, &req[i]);
810         if (state == NSTACKX_EFAILED) {
811             (void)close(fd);
812             return;
813         } else if (state == NSTACKX_EINVAL) {
814             continue;
815         }
816 
817         uint8_t ifaceType = GetIfaceType(req[i].ifr_name);
818         if (ifaceType > IFACE_TYPE_WLAN) {
819             DFINDER_LOGI(TAG, "skip iface %s", req[i].ifr_name);
820             continue;
821         }
822 
823         union InetAddr addr;
824         addr.in.s_addr = (((struct sockaddr_in *)&req[i].ifr_addr)->sin_addr).s_addr;
825         if (req[i].ifr_addr.sa_family != AF_INET || addr.in.s_addr == 0) {
826             DFINDER_LOGI(TAG, "iface %s is not ipv4 or ip is any", req[i].ifr_name);
827             continue;
828         }
829 
830         DFINDER_LOGI(TAG, "try to add new iface %s", req[i].ifr_name);
831         char serviceData[NSTACKX_MAX_SERVICE_DATA_LEN];
832         (void)memset_s(serviceData, NSTACKX_MAX_SERVICE_DATA_LEN, 0, NSTACKX_MAX_SERVICE_DATA_LEN);
833         (void)AddLocalIface(req[i].ifr_name, serviceData, AF_INET, &addr);
834     }
835     (void)close(fd);
836 
837     (void)arg;
838 }
839 #endif /* _WIN32 */
840 
841 #endif /* END OF DFINDER_USE_MINI_NSTACKX */
842 
GetBroadcastIp(const struct LocalIface * iface,char * ipStr,size_t ipStrLen)843 int GetBroadcastIp(const struct LocalIface *iface, char *ipStr, size_t ipStrLen)
844 {
845 #ifdef _WIN32
846     return GetIfBroadcastAddr(&iface->ip, ipStr, ipStrLen);
847 #else
848     if (iface->af == AF_INET) {
849         return GetIfBroadcastIp(iface->ifname, ipStr, ipStrLen);
850     }
851 
852     // ipv6 not support broadcast, only use multicast
853     if (strcpy_s(ipStr, ipStrLen, NSTACKX_IPV6_MULTICAST_ADDR) != EOK) {
854         DFINDER_LOGE(TAG, "strcpy_s failed");
855         return NSTACKX_EFAILED;
856     }
857 
858     return NSTACKX_EOK;
859 #endif
860 }
861 
GetLocalDeviceId(void)862 const char *GetLocalDeviceId(void)
863 {
864     return g_localDevice.deviceInfo.deviceId;
865 }
866 
GetLocalDeviceInfo(void)867 DeviceInfo *GetLocalDeviceInfo(void)
868 {
869     return &g_localDevice.deviceInfo;
870 }
871 
GetLocalDeviceNetworkName(void)872 const char *GetLocalDeviceNetworkName(void)
873 {
874     return g_localDevice.deviceInfo.networkName;
875 }
876 
GetLocalIfaceIpStr(const struct LocalIface * iface)877 const char *GetLocalIfaceIpStr(const struct LocalIface *iface)
878 {
879     return iface->ipStr;
880 }
881 
GetLocalIfaceServiceData(const struct LocalIface * iface)882 const char *GetLocalIfaceServiceData(const struct LocalIface *iface)
883 {
884     return iface->serviceData;
885 }
886 
GetLocalIfaceAf(const struct LocalIface * iface)887 uint8_t GetLocalIfaceAf(const struct LocalIface *iface)
888 {
889     return iface->af;
890 }
891 
GetLocalIfaceName(const struct LocalIface * iface)892 const char *GetLocalIfaceName(const struct LocalIface *iface)
893 {
894     return iface->ifname;
895 }
896 
LocalIfaceGetCoapCtx(uint8_t af,const char * ifname)897 CoapCtxType *LocalIfaceGetCoapCtx(uint8_t af, const char *ifname)
898 {
899     int i;
900     for (i = 0; i < IFACE_TYPE_MAX; i++) {
901         List *pos = NULL;
902         LIST_FOR_EACH(pos, &g_localDevice.readyList[i]) {
903             struct LocalIface *iface = (struct LocalIface *)pos;
904             if (af != iface->af || strcmp(iface->ifname, ifname) != 0) {
905                 continue;
906             }
907 
908             return iface->ctx;
909         }
910     }
911 
912     return NULL;
913 }
914 
915 #ifdef _WIN32
GetLocalIfaceByLocalIp(const struct in_addr * ip)916 static struct LocalIface *GetLocalIfaceByLocalIp(const struct in_addr *ip)
917 {
918     int i;
919     for (i = 0; i < IFACE_TYPE_MAX; i++) {
920         List *pos = NULL;
921         LIST_FOR_EACH(pos, &g_localDevice.readyList[i]) {
922             struct LocalIface *iface = (struct LocalIface *)pos;
923             if (iface->ip.s_addr != ip->s_addr) {
924                 continue;
925             }
926 
927             return iface;
928         }
929     }
930 
931     return NULL;
932 }
933 #endif
934 
935 #ifndef DFINDER_USE_MINI_NSTACKX
IfaceTypeIsMatch(uint8_t ifaceType,uint8_t serverType)936 static inline bool IfaceTypeIsMatch(uint8_t ifaceType, uint8_t serverType)
937 {
938     return serverType == INVALID_TYPE ||
939         (serverType == SERVER_TYPE_WLANORETH && (ifaceType == IFACE_TYPE_ETH || ifaceType == IFACE_TYPE_WLAN)) ||
940         (serverType == SERVER_TYPE_P2P && ifaceType == IFACE_TYPE_P2P) ||
941         (serverType == SERVER_TYPE_USB && ifaceType == IFACE_TYPE_USB) ||
942         (serverType == IFACE_TYPE_USB_SCALE && ifaceType == IFACE_TYPE_USB_SCALE);
943 }
944 
LocalIfaceGetCoapCtxByRemoteIp(const struct in_addr * remoteIp,uint8_t serverType)945 CoapCtxType *LocalIfaceGetCoapCtxByRemoteIp(const struct in_addr *remoteIp, uint8_t serverType)
946 {
947     struct LocalIface *iface = NULL;
948     struct sockaddr_in addr;
949     addr.sin_addr.s_addr = remoteIp->s_addr;
950 #ifdef _WIN32
951     InterfaceInfo localDev;
952     (void)memset_s(&localDev, sizeof(InterfaceInfo), 0, sizeof(InterfaceInfo));
953     if (GetTargetAdapter(&addr, &localDev) != NSTACKX_EOK) {
954         DFINDER_LOGE(TAG, "get target adapter failed");
955         return NULL;
956     }
957     struct in_addr localIp = { .s_addr = localDev.ipAddr };
958     iface = GetLocalIfaceByLocalIp(&localIp);
959 #else
960     struct ifreq req;
961     (void)memset_s(&req, sizeof(struct ifreq), 0, sizeof(struct ifreq));
962     if (GetTargetInterface(&addr, &req) != NSTACKX_EOK) {
963         DFINDER_LOGE(TAG, "get target interface failed");
964         return NULL;
965     }
966 
967     uint8_t ifaceType = GetIfaceType(req.ifr_ifrn.ifrn_name);
968     DFINDER_LOGD(TAG, "ifaceType: %hhu", ifaceType);
969     iface = GetLocalIface(&g_localDevice.readyList[ifaceType], req.ifr_ifrn.ifrn_name, AF_INET, NULL);
970 #endif
971     if (iface == NULL) {
972         DFINDER_LOGE(TAG, "can not find iface");
973         return NULL;
974     }
975 
976     if (!IfaceTypeIsMatch(iface->type, serverType)) {
977         DFINDER_LOGE(TAG, "type not match, iface type: %hhu, server type: %hhu", iface->type, serverType);
978         return NULL;
979     }
980 
981     return iface->ctx;
982 }
983 
984 #else
GetLocalIfaceIp(const struct LocalIface * iface)985 const struct in_addr *GetLocalIfaceIp(const struct LocalIface *iface)
986 {
987     return &iface->in;
988 }
989 #endif
990 
991 #ifdef NSTACKX_DFINDER_HIDUMP
992 
LocalSetDumpIfaceInfo(struct DumpIfaceInfo * info,const char * ifname,uint8_t state,uint8_t af,union InetAddr * addr)993 static inline struct DumpIfaceInfo *LocalSetDumpIfaceInfo(struct DumpIfaceInfo *info, const char *ifname, uint8_t state,
994     uint8_t af, union InetAddr *addr)
995 {
996     info->ifname = ifname;
997     info->state = state;
998     info->af = af;
999     info->addr = addr;
1000     return info;
1001 }
1002 
LocalIfaceDump(char * buf,size_t size)1003 int LocalIfaceDump(char *buf, size_t size)
1004 {
1005     List *pos = NULL;
1006     struct DumpIfaceInfo info;
1007     struct LocalIface *iface = NULL;
1008     int ret;
1009     size_t index = 0;
1010     int i;
1011     for (i = 0; i < IFACE_TYPE_MAX; i++) {
1012         LIST_FOR_EACH(pos, &g_localDevice.readyList[i]) {
1013             iface = (struct LocalIface *)pos;
1014 
1015             ret = DFinderDumpIface(LocalSetDumpIfaceInfo(&info, iface->ifname, iface->state, iface->af, &iface->addr),
1016                 buf + index, size - index);
1017             if (ret < 0 || (uint32_t)ret > size - index) {
1018                 return NSTACKX_EFAILED;
1019             }
1020 
1021             index += (uint32_t)ret;
1022         }
1023     }
1024 
1025     LIST_FOR_EACH(pos, &g_localDevice.creatingList) {
1026         iface = (struct LocalIface *)pos;
1027         ret = DFinderDumpIface(LocalSetDumpIfaceInfo(&info, iface->ifname, iface->state, iface->af, &iface->addr),
1028             buf + index, size - index);
1029         if (ret < 0 || (uint32_t)ret > size - index) {
1030             return NSTACKX_EFAILED;
1031         }
1032 
1033         index += (uint32_t)ret;
1034     }
1035 
1036     LIST_FOR_EACH(pos, &g_localDevice.destroyList) {
1037         iface = (struct LocalIface *)pos;
1038         ret = DFinderDumpIface(LocalSetDumpIfaceInfo(&info, iface->ifname, iface->state, iface->af, &iface->addr),
1039             buf + index, size - index);
1040         if (ret < 0 || (uint32_t)ret > size - index) {
1041             return NSTACKX_EFAILED;
1042         }
1043 
1044         index += (uint32_t)ret;
1045     }
1046 
1047     return index;
1048 }
1049 #endif
1050