]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCNetworkConfigurationInternal.c
configd-1061.141.1.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / SCNetworkConfigurationInternal.c
1 /*
2 * Copyright (c) 2004-2007, 2009, 2010-2013, 2015-2020 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * Modification History
26 *
27 * May 27, 2004 Allan Nathanson <ajn@apple.com>
28 * - initial revision
29 */
30
31
32 #include "SCNetworkConfigurationInternal.h"
33 #include "SCPreferencesInternal.h"
34
35 #include <sys/ioctl.h>
36 #include <net/if.h>
37
38
39 __private_extern__ os_log_t
40 __log_SCNetworkConfiguration(void)
41 {
42 static os_log_t log = NULL;
43
44 if (log == NULL) {
45 log = os_log_create("com.apple.SystemConfiguration", "SCNetworkConfiguration");
46 }
47
48 return log;
49 }
50
51
52 static Boolean
53 isEffectivelyEmptyConfiguration(CFDictionaryRef config)
54 {
55 Boolean empty = FALSE;
56 CFIndex n;
57
58 n = isA_CFDictionary(config) ? CFDictionaryGetCount(config) : 0;
59 switch (n) {
60 case 0 :
61 // if no keys
62 empty = TRUE;
63 break;
64 case 1 :
65 if (CFDictionaryContainsKey(config, kSCResvInactive)) {
66 // ignore [effectively] empty configuration entities
67 empty = TRUE;
68 }
69 break;
70 default :
71 break;
72 }
73
74 return empty;
75 }
76
77
78 __private_extern__ CFDictionaryRef
79 __SCNetworkConfigurationGetValue(SCPreferencesRef prefs, CFStringRef path)
80 {
81 CFDictionaryRef config;
82
83 config = SCPreferencesPathGetValue(prefs, path);
84 if (isEffectivelyEmptyConfiguration(config)) {
85 // ignore [effectively] empty configuration entities
86 config = NULL;
87 }
88
89 return config;
90 }
91
92
93 __private_extern__ Boolean
94 __SCNetworkConfigurationSetValue(SCPreferencesRef prefs,
95 CFStringRef path,
96 CFDictionaryRef config,
97 Boolean keepInactive)
98 {
99 Boolean changed;
100 CFDictionaryRef curConfig;
101 CFMutableDictionaryRef newConfig = NULL;
102 Boolean ok;
103
104 if ((config != NULL) && !isA_CFDictionary(config)) {
105 _SCErrorSet(kSCStatusInvalidArgument);
106 return FALSE;
107 }
108
109 curConfig = SCPreferencesPathGetValue(prefs, path);
110 curConfig = isA_CFDictionary(curConfig);
111
112 if (config != NULL) {
113 newConfig = CFDictionaryCreateMutableCopy(NULL, 0, config);
114 }
115
116 if (keepInactive) {
117 if (config == NULL) {
118 newConfig = CFDictionaryCreateMutable(NULL,
119 0,
120 &kCFTypeDictionaryKeyCallBacks,
121 &kCFTypeDictionaryValueCallBacks);
122 }
123
124 if (isA_CFDictionary(curConfig) && CFDictionaryContainsKey(curConfig, kSCResvInactive)) {
125 // if currently disabled
126 CFDictionarySetValue(newConfig, kSCResvInactive, kCFBooleanTrue);
127 } else {
128 // if currently enabled
129 CFDictionaryRemoveValue(newConfig, kSCResvInactive);
130 }
131 }
132
133 // check if the configuration changed
134 changed = !_SC_CFEqual(curConfig, newConfig);
135
136 // set new configuration
137 if (!changed) {
138 // if no change
139 if (newConfig != NULL) CFRelease(newConfig);
140 ok = TRUE;
141 } else if (newConfig != NULL) {
142 // if new configuration (or we are preserving a disabled state), update the prefs
143 ok = SCPreferencesPathSetValue(prefs, path, newConfig);
144 CFRelease(newConfig);
145 } else {
146 // update the prefs
147 ok = SCPreferencesPathRemoveValue(prefs, path);
148 if (!ok && (SCError() == kSCStatusNoKey)) {
149 ok = TRUE;
150 }
151 }
152
153 return ok;
154 }
155
156
157 __private_extern__ Boolean
158 __getPrefsEnabled(SCPreferencesRef prefs, CFStringRef path)
159 {
160 CFDictionaryRef config;
161
162 config = SCPreferencesPathGetValue(prefs, path);
163 if (isA_CFDictionary(config) && CFDictionaryContainsKey(config, kSCResvInactive)) {
164 return FALSE;
165 }
166
167 return TRUE;
168 }
169
170
171 __private_extern__ Boolean
172 __setPrefsEnabled(SCPreferencesRef prefs,
173 CFStringRef path,
174 Boolean enabled)
175 {
176 Boolean changed;
177 CFDictionaryRef curConfig;
178 CFMutableDictionaryRef newConfig = NULL;
179 Boolean ok = FALSE;
180
181 // preserve current configuration
182 curConfig = SCPreferencesPathGetValue(prefs, path);
183 if (curConfig != NULL) {
184 if (!isA_CFDictionary(curConfig)) {
185 _SCErrorSet(kSCStatusFailed);
186 return FALSE;
187 }
188 newConfig = CFDictionaryCreateMutableCopy(NULL, 0, curConfig);
189
190 if (enabled) {
191 // enable
192 CFDictionaryRemoveValue(newConfig, kSCResvInactive);
193 } else {
194 // disable
195 CFDictionarySetValue(newConfig, kSCResvInactive, kCFBooleanTrue);
196 }
197 } else {
198 if (!enabled) {
199 // disable
200 newConfig = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
201 CFDictionarySetValue(newConfig, kSCResvInactive, kCFBooleanTrue);
202 }
203 }
204
205 // check if the configuration changed
206 changed = !_SC_CFEqual(curConfig, newConfig);
207
208 // set new configuration
209 if (!changed) {
210 // if no change
211 if (newConfig != NULL) CFRelease(newConfig);
212 ok = TRUE;
213 } else if (newConfig != NULL) {
214 // if updated configuration (or we are establishing as disabled)
215
216 ok = SCPreferencesPathSetValue(prefs, path, newConfig);
217 CFRelease(newConfig);
218 } else {
219 ok = SCPreferencesPathRemoveValue(prefs, path);
220 if (!ok && (SCError() == kSCStatusNoKey)) {
221 ok = TRUE;
222 }
223 }
224
225 return ok;
226 }
227
228 #if TARGET_OS_OSX
229 #define SYSTEMCONFIGURATION_RESOURCES_PATH SYSTEMCONFIGURATION_FRAMEWORK_PATH "/Resources"
230 #else
231 #define SYSTEMCONFIGURATION_RESOURCES_PATH SYSTEMCONFIGURATION_FRAMEWORK_PATH
232 #endif // TARGET_OS_OSX
233
234 #define NETWORKCONFIGURATION_RESOURCE_FILE "NetworkConfiguration.plist"
235
236 static CFDictionaryRef
237 __copyTemplates()
238 {
239 CFBundleRef bundle;
240 CFDictionaryRef templates;
241 CFURLRef url;
242
243 bundle = _SC_CFBundleGet();
244 if (bundle == NULL) {
245 return NULL;
246 }
247
248 url = CFBundleCopyResourceURL(bundle, CFSTR("NetworkConfiguration"), CFSTR("plist"), NULL);
249 if (url == NULL) {
250 SC_log(LOG_ERR, "failed to GET resource URL to \"%s\". Trying harder...", NETWORKCONFIGURATION_RESOURCE_FILE);
251 url = CFURLCreateWithFileSystemPath(NULL,
252 CFSTR(SYSTEMCONFIGURATION_RESOURCES_PATH
253 "/"
254 NETWORKCONFIGURATION_RESOURCE_FILE),
255 kCFURLPOSIXPathStyle,
256 TRUE);
257
258 if (url == NULL) {
259 SC_log(LOG_ERR, "failed to CREATE resource URL to \"%s\"", SYSTEMCONFIGURATION_RESOURCES_PATH
260 "/"
261 NETWORKCONFIGURATION_RESOURCE_FILE);
262 return NULL;
263 }
264 }
265
266 templates = _SCCreatePropertyListFromResource(url);
267 CFRelease(url);
268
269 if ((templates != NULL) && !isA_CFDictionary(templates)) {
270 CFRelease(templates);
271 return NULL;
272 }
273
274 return templates;
275 }
276
277
278 __private_extern__ CFDictionaryRef
279 __copyInterfaceTemplate(CFStringRef interfaceType,
280 CFStringRef childInterfaceType)
281 {
282 CFDictionaryRef interface = NULL;
283 CFDictionaryRef interfaces;
284 CFDictionaryRef templates;
285
286 templates = __copyTemplates();
287 if (templates == NULL) {
288 return NULL;
289 }
290
291 interfaces = CFDictionaryGetValue(templates, CFSTR("Interface"));
292 if (!isA_CFDictionary(interfaces)) {
293 CFRelease(templates);
294 return NULL;
295 }
296
297 if (childInterfaceType == NULL) {
298 interface = CFDictionaryGetValue(interfaces, interfaceType);
299 } else {
300 CFStringRef expandedType;
301
302 if (CFStringFind(childInterfaceType, CFSTR("."), 0).location != kCFNotFound) {
303 // if "vendor" type
304 childInterfaceType = CFSTR("*");
305 }
306
307 expandedType = CFStringCreateWithFormat(NULL,
308 NULL,
309 CFSTR("%@-%@"),
310 interfaceType,
311 childInterfaceType);
312 interface = CFDictionaryGetValue(interfaces, expandedType);
313 CFRelease(expandedType);
314 }
315
316 if (isA_CFDictionary(interface) && (CFDictionaryGetCount(interface) > 0)) {
317 CFRetain(interface);
318 } else {
319 interface = NULL;
320 }
321
322 CFRelease(templates);
323
324 return interface;
325 }
326
327
328 __private_extern__ CFDictionaryRef
329 __copyProtocolTemplate(CFStringRef interfaceType,
330 CFStringRef childInterfaceType,
331 CFStringRef protocolType)
332 {
333 CFDictionaryRef interface = NULL;
334 CFDictionaryRef protocol = NULL;
335 CFDictionaryRef protocols;
336 CFDictionaryRef templates;
337
338 templates = __copyTemplates();
339 if (templates == NULL) {
340 return NULL;
341 }
342
343 protocols = CFDictionaryGetValue(templates, CFSTR("Protocol"));
344 if (!isA_CFDictionary(protocols)) {
345 CFRelease(templates);
346 return NULL;
347 }
348
349 if (childInterfaceType == NULL) {
350 interface = CFDictionaryGetValue(protocols, interfaceType);
351 } else {
352 CFStringRef expandedType;
353
354 if (CFStringFind(childInterfaceType, CFSTR("."), 0).location != kCFNotFound) {
355 // if "vendor" type
356 childInterfaceType = CFSTR("*");
357 }
358
359 expandedType = CFStringCreateWithFormat(NULL,
360 NULL,
361 CFSTR("%@-%@"),
362 interfaceType,
363 childInterfaceType);
364 interface = CFDictionaryGetValue(protocols, expandedType);
365 CFRelease(expandedType);
366 }
367
368 if (isA_CFDictionary(interface)) {
369 protocol = CFDictionaryGetValue(interface, protocolType);
370 if (isA_CFDictionary(protocol)) {
371 CFRetain(protocol);
372 } else {
373 protocol = NULL;
374 }
375 }
376
377 CFRelease(templates);
378
379 return protocol;
380 }
381
382
383 __private_extern__ Boolean
384 __createInterface(int s, CFStringRef interface)
385 {
386 struct ifreq ifr;
387
388 memset(&ifr, 0, sizeof(ifr));
389 (void) _SC_cfstring_to_cstring(interface,
390 ifr.ifr_name,
391 sizeof(ifr.ifr_name),
392 kCFStringEncodingASCII);
393
394 if (ioctl(s, SIOCIFCREATE, &ifr) == -1) {
395 SC_log(LOG_NOTICE, "could not create interface \"%@\": %s",
396 interface,
397 strerror(errno));
398 return FALSE;
399 }
400
401 return TRUE;
402 }
403
404
405 __private_extern__ Boolean
406 __destroyInterface(int s, CFStringRef interface)
407 {
408 struct ifreq ifr;
409
410 memset(&ifr, 0, sizeof(ifr));
411 (void) _SC_cfstring_to_cstring(interface,
412 ifr.ifr_name,
413 sizeof(ifr.ifr_name),
414 kCFStringEncodingASCII);
415
416 if (ioctl(s, SIOCIFDESTROY, &ifr) == -1) {
417 SC_log(LOG_NOTICE, "could not destroy interface \"%@\": %s",
418 interface,
419 strerror(errno));
420 return FALSE;
421 }
422
423 return TRUE;
424 }
425
426
427 /*
428 * For rdar://problem/4685223
429 *
430 * To keep MoreSCF happy we need to ensure that the first "Set" and
431 * "NetworkService" have a [less than] unique identifier that can
432 * be parsed as a numeric string.
433 *
434 * Note: this backwards compatibility code must be enabled using the
435 * following command:
436 *
437 * sudo defaults write \
438 * /Library/Preferences/SystemConfiguration/preferences \
439 * MoreSCF \
440 * -bool true
441 */
442 __private_extern__
443 CFStringRef
444 __SCPreferencesPathCreateUniqueChild_WithMoreSCFCompatibility(SCPreferencesRef prefs, CFStringRef prefix)
445 {
446 static int hack = -1;
447 CFStringRef path = NULL;
448
449 if (hack < 0) {
450 CFBooleanRef enable;
451
452 enable = SCPreferencesGetValue(prefs, CFSTR("MoreSCF"));
453 hack = (isA_CFBoolean(enable) && CFBooleanGetValue(enable)) ? 1 : 0;
454 }
455
456 if (hack > 0) {
457 CFDictionaryRef dict;
458 Boolean ok;
459
460 path = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@/%@"), prefix, CFSTR("0"));
461 dict = SCPreferencesPathGetValue(prefs, path);
462 if (dict != NULL) {
463 // if path "0" exists
464 CFRelease(path);
465 return NULL;
466 }
467
468 // unique child with path "0" does not exist, create
469 dict = CFDictionaryCreate(NULL,
470 NULL, NULL, 0,
471 &kCFTypeDictionaryKeyCallBacks,
472 &kCFTypeDictionaryValueCallBacks);
473 ok = SCPreferencesPathSetValue(prefs, path, dict);
474 CFRelease(dict);
475 if (!ok) {
476 // if create failed
477 CFRelease(path);
478 return NULL;
479 }
480 }
481
482 return path;
483 }
484
485
486 static CFDataRef
487 __copy_legacy_password(CFTypeRef password)
488 {
489 if (password == NULL) {
490 return NULL;
491 }
492
493 if (isA_CFData(password)) {
494 CFIndex n;
495
496 n = CFDataGetLength(password);
497 if ((n % sizeof(UniChar)) == 0) {
498 CFStringEncoding encoding;
499 CFStringRef str;
500
501 #if __BIG_ENDIAN__
502 encoding = (*(CFDataGetBytePtr(password) + 1) == 0x00) ? kCFStringEncodingUTF16LE : kCFStringEncodingUTF16BE;
503 #else // __LITTLE_ENDIAN__
504 encoding = (*(CFDataGetBytePtr(password) ) == 0x00) ? kCFStringEncodingUTF16BE : kCFStringEncodingUTF16LE;
505 #endif
506 str = CFStringCreateWithBytes(NULL,
507 (const UInt8 *)CFDataGetBytePtr(password),
508 n,
509 encoding,
510 FALSE);
511 password = CFStringCreateExternalRepresentation(NULL,
512 str,
513 kCFStringEncodingUTF8,
514 0);
515 CFRelease(str);
516 } else {
517 password = NULL;
518 }
519 } else if (isA_CFString(password) && (CFStringGetLength(password) > 0)) {
520 // convert password to CFData
521 password = CFStringCreateExternalRepresentation(NULL,
522 password,
523 kCFStringEncodingUTF8,
524 0);
525 } else {
526 password = NULL;
527 }
528
529 return password;
530 }
531
532
533 __private_extern__
534 Boolean
535 __extract_password(SCPreferencesRef prefs,
536 CFDictionaryRef config,
537 CFStringRef passwordKey,
538 CFStringRef encryptionKey,
539 CFStringRef encryptionKeyChainValue,
540 CFStringRef unique_id,
541 CFDataRef *password)
542 {
543 CFStringRef encryption = NULL;
544 Boolean exists = FALSE;
545
546 // check for keychain password
547 if (config != NULL) {
548 encryption = CFDictionaryGetValue(config, encryptionKey);
549 }
550 if ((encryption == NULL) ||
551 (isA_CFString(encryption) &&
552 CFEqual(encryption, encryptionKeyChainValue))) {
553 // check password
554 if (password != NULL) {
555 if (prefs != NULL) {
556 *password = _SCPreferencesSystemKeychainPasswordItemCopy(prefs, unique_id);
557 } else {
558 *password = _SCSecKeychainPasswordItemCopy(NULL, unique_id);
559 }
560 exists = (*password != NULL);
561 } else {
562 if (prefs != NULL) {
563 exists = _SCPreferencesSystemKeychainPasswordItemExists(prefs, unique_id);
564 } else {
565 exists = _SCSecKeychainPasswordItemExists(NULL, unique_id);
566 }
567 }
568 }
569
570 // as needed, check for in-line password
571 if (!exists && (encryption == NULL) && (config != NULL)) {
572 CFDataRef inline_password;
573
574 inline_password = CFDictionaryGetValue(config, passwordKey);
575 inline_password = __copy_legacy_password(inline_password);
576 if (inline_password != NULL) {
577 exists = TRUE;
578
579 if (password != NULL) {
580 *password = inline_password;
581 } else {
582 CFRelease(inline_password);
583 }
584 }
585 }
586
587 return exists;
588 }
589
590
591 __private_extern__
592 Boolean
593 __remove_password(SCPreferencesRef prefs,
594 CFDictionaryRef config,
595 CFStringRef passwordKey,
596 CFStringRef encryptionKey,
597 CFStringRef encryptionKeyChainValue,
598 CFStringRef unique_id,
599 CFDictionaryRef *newConfig)
600 {
601 CFStringRef encryption = NULL;
602 Boolean ok = FALSE;
603
604 // check for keychain password
605 if (config != NULL) {
606 encryption = CFDictionaryGetValue(config, encryptionKey);
607 }
608 if ((encryption == NULL) ||
609 (isA_CFString(encryption) &&
610 CFEqual(encryption, encryptionKeyChainValue))) {
611 // remove keychain password
612 if (prefs != NULL) {
613 ok = _SCPreferencesSystemKeychainPasswordItemRemove(prefs, unique_id);
614 } else {
615 ok = _SCSecKeychainPasswordItemRemove(NULL, unique_id);
616 }
617 }
618
619 // as needed, check if we have an in-line password that we can remove
620 if (!ok && (encryption == NULL) && (config != NULL)) {
621 CFDataRef inline_password;
622
623 inline_password = CFDictionaryGetValue(config, passwordKey);
624 inline_password = __copy_legacy_password(inline_password);
625 if (inline_password != NULL) {
626 CFRelease(inline_password);
627 ok = TRUE;
628 }
629 }
630
631 if (newConfig != NULL) {
632 if (ok && (config != NULL)) {
633 CFMutableDictionaryRef temp;
634
635 temp = CFDictionaryCreateMutableCopy(NULL, 0, config);
636 CFDictionaryRemoveValue(temp, passwordKey);
637 CFDictionaryRemoveValue(temp, encryptionKey);
638 *newConfig = (CFDictionaryRef)temp;
639 } else {
640 *newConfig = NULL;
641 }
642 }
643
644 return ok;
645 }
646
647
648 __private_extern__ Boolean
649 __rank_to_str(SCNetworkServicePrimaryRank rank, CFStringRef *rankStr)
650 {
651 switch (rank) {
652 case kSCNetworkServicePrimaryRankDefault :
653 *rankStr = NULL;
654 break;
655 case kSCNetworkServicePrimaryRankFirst :
656 *rankStr = kSCValNetServicePrimaryRankFirst;
657 break;
658 case kSCNetworkServicePrimaryRankLast :
659 *rankStr = kSCValNetServicePrimaryRankLast;
660 break;
661 case kSCNetworkServicePrimaryRankNever :
662 *rankStr = kSCValNetServicePrimaryRankNever;
663 break;
664 case kSCNetworkServicePrimaryRankScoped :
665 *rankStr = kSCValNetServicePrimaryRankScoped;
666 break;
667 default :
668 return FALSE;
669 }
670
671 return TRUE;
672 }
673
674
675 __private_extern__ Boolean
676 __str_to_rank(CFStringRef rankStr, SCNetworkServicePrimaryRank *rank)
677 {
678 if (isA_CFString(rankStr)) {
679 if (CFEqual(rankStr, kSCValNetServicePrimaryRankFirst)) {
680 *rank = kSCNetworkServicePrimaryRankFirst;
681 } else if (CFEqual(rankStr, kSCValNetServicePrimaryRankLast)) {
682 *rank = kSCNetworkServicePrimaryRankLast;
683 } else if (CFEqual(rankStr, kSCValNetServicePrimaryRankNever)) {
684 *rank = kSCNetworkServicePrimaryRankNever;
685 } else if (CFEqual(rankStr, kSCValNetServicePrimaryRankScoped)) {
686 *rank = kSCNetworkServicePrimaryRankScoped;
687 } else {
688 return FALSE;
689 }
690 } else if (rankStr == NULL) {
691 *rank = kSCNetworkServicePrimaryRankDefault;
692 } else {
693 return FALSE;
694 }
695
696 return TRUE;
697 }
698
699
700 #define kSCNetworkConfigurationFlagsBypassSystemInterfaces (1<<0)
701 #define kSCNetworkConfigurationFlagsBypassSystemInterfacesForced (1<<1)
702
703
704 Boolean
705 _SCNetworkConfigurationBypassSystemInterfaces(SCPreferencesRef prefs)
706 {
707 Boolean bypass;
708 uint32_t nc_flags;
709
710 nc_flags = __SCPreferencesGetNetworkConfigurationFlags(prefs);
711 bypass = ((nc_flags & kSCNetworkConfigurationFlagsBypassSystemInterfaces) != 0);
712 if (bypass ||
713 (nc_flags & kSCNetworkConfigurationFlagsBypassSystemInterfacesForced) != 0) {
714 // if bypass flag explicitly requested
715 return bypass;
716 }
717
718 if (!__SCPreferencesUsingDefaultPrefs(prefs)) {
719 // if not using the default prefs (i.e. /L/P/SC/preferences.plist)
720 return TRUE;
721 }
722
723 return FALSE;
724 }
725
726
727 void
728 _SCNetworkConfigurationSetBypassSystemInterfaces(SCPreferencesRef prefs, Boolean shouldBypass)
729 {
730 uint32_t nc_flags;
731
732 nc_flags = __SCPreferencesGetNetworkConfigurationFlags(prefs);
733 if (shouldBypass) {
734 nc_flags |= kSCNetworkConfigurationFlagsBypassSystemInterfaces;
735 } else {
736 nc_flags &= ~kSCNetworkConfigurationFlagsBypassSystemInterfaces;
737 }
738 nc_flags |= kSCNetworkConfigurationFlagsBypassSystemInterfacesForced;
739 __SCPreferencesSetNetworkConfigurationFlags(prefs, nc_flags);
740
741 return;
742 }
743
744