3 * Copyright (c) 2020 Apple Computer, Inc. All rights reserved.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 * This file contains the implementation of the SRP Advertising Proxy management
18 * API on MacOS, which is private API used to control and manage the advertising
25 #include <netinet/in.h>
27 #include <netinet6/in6_var.h>
28 #include <netinet/icmp6.h>
29 #include <netinet6/nd6.h>
30 #include "xpc_clients.h"
32 #define THREAD_SERVICE_SEND_BOTH 1
34 #include "cti-services.h"
36 //*************************************************************************************************************
39 static int client_serial_number
;
43 cti_tunnel_reply_t tunnel_reply
;
44 cti_service_reply_t service_reply
;
45 cti_prefix_reply_t prefix_reply
;
46 cti_state_reply_t state_reply
;
47 cti_partition_id_reply_t partition_id_reply
;
48 cti_network_node_type_reply_t network_node_type_reply
;
52 (*cti_internal_callback_t
)(cti_connection_t NONNULL conn_ref
, xpc_object_t reply
, cti_status_t status
);
54 struct _cti_connection_t
58 // xpc_connection between client and daemon
59 xpc_connection_t NULLABLE connection
;
61 // Callback function ptr for Client
62 cti_callback_t callback
;
64 // Before we can send commands, we have to check in, so when starting up, we stash the initial command
65 // here until we get an acknowledgment for the checkin.
66 xpc_object_t
*first_command
;
68 // For commands that fetch properties and also track properties, this will contain the name of the property
69 // for which events are requested.
70 const char *property_name
;
72 cti_internal_callback_t NONNULL internal_callback
;
74 // Queue specified by client for scheduling its Callback
75 dispatch_queue_t NONNULL client_queue
;
78 void *NULLABLE context
;
80 // Printed when debugging the event handler
81 const char *NONNULL command_name
;
83 // True if we've gotten a response to the check-in message.
87 //*************************************************************************************************************
91 cti_connection_finalize(cti_connection_t ref
)
96 #define cti_connection_release(ref) cti_connection_release_(ref, __FILE__, __LINE__)
98 cti_connection_release_(cti_connection_t ref
, const char *file
, int line
)
100 ref
->callback
.reply
= NULL
;
101 if (ref
->connection
!= NULL
) {
102 xpc_connection_cancel(ref
->connection
);
104 RELEASE(ref
, cti_connection_finalize
);
108 cti_xpc_connection_finalize(void *context
)
110 cti_connection_release(context
);
114 cti_xpc_copy_description(xpc_object_t object
)
116 xpc_type_t type
= xpc_get_type(object
);
117 if (type
== XPC_TYPE_UINT64
) {
118 uint64_t num
= xpc_uint64_get_value(object
);
120 snprintf(buf
, sizeof buf
, "%llu", num
);
122 } else if (type
== XPC_TYPE_INT64
) {
123 int64_t num
= xpc_int64_get_value(object
);
125 snprintf(buf
, sizeof buf
, "%lld", num
);
127 } else if (type
== XPC_TYPE_STRING
) {
128 const char *str
= xpc_string_get_string_ptr(object
);
129 size_t len
= xpc_string_get_length(object
);
130 char *ret
= malloc(len
+ 3);
133 strlcpy(ret
+ 1, str
, len
+ 1);
138 } else if (type
== XPC_TYPE_DATA
) {
139 const uint8_t *data
= xpc_data_get_bytes_ptr(object
);
140 size_t i
, len
= xpc_data_get_length(object
);
141 char *ret
= malloc(len
* 2 + 3);
145 for (i
= 0; i
< len
; i
++) {
146 snprintf(ret
+ i
* 2, 3, "%02x", data
[i
]);
150 } else if (type
== XPC_TYPE_BOOL
) {
151 bool flag
= xpc_bool_get_value(object
);
153 return strdup("true");
155 return strdup("false");
157 } else if (type
== XPC_TYPE_ARRAY
) {
158 size_t avail
, vlen
, len
= 0, i
, count
= xpc_array_get_count(object
);
159 char **values
= malloc(count
* sizeof(*values
));
161 if (values
== NULL
) {
164 xpc_array_apply(object
, ^bool (size_t index
, xpc_object_t value
) {
165 values
[index
] = cti_xpc_copy_description(value
);
168 for (i
= 0; i
< count
; i
++) {
169 if (values
[i
] == NULL
) {
172 len
+= strlen(values
[i
]) + 2;
175 ret
= malloc(len
+ 3);
180 for (i
= 0; i
< count
; i
++) {
182 snprintf(p_ret
, avail
, "%s%s%s", i
== 0 ? "" : " ", values
[i
] != NULL
? values
[i
] : "NULL", (i
+ 1 == count
) ? "" : ",");
183 vlen
= strlen(p_ret
);
187 if (values
[i
] != NULL
) {
196 return xpc_copy_description(object
);
200 cti_log_object(const char *context
, const char *command
, const char *preamble
, const char *divide
, xpc_object_t
*object
, char *indent
)
202 xpc_type_t type
= xpc_get_type(object
);
203 static char no_indent
[] = "";
204 if (indent
== NULL
) {
210 char *compound_begin
= "";
211 char *compound_end
= "";
213 if (type
== XPC_TYPE_DICTIONARY
|| type
== XPC_TYPE_ARRAY
) {
215 bool *p_compact
= &compact
;
216 if (type
== XPC_TYPE_DICTIONARY
) {
217 compound_begin
= "{";
219 xpc_dictionary_apply(object
, ^bool (const char *__unused key
, xpc_object_t value
) {
220 xpc_type_t sub_type
= xpc_get_type(value
);
221 if (sub_type
== XPC_TYPE_DICTIONARY
) {
223 } else if (sub_type
== XPC_TYPE_ARRAY
) {
224 xpc_array_apply(value
, ^bool (size_t __unused index
, xpc_object_t sub_value
) {
225 xpc_type_t sub_sub_type
= xpc_get_type(sub_value
);
226 if (sub_sub_type
== XPC_TYPE_DICTIONARY
|| sub_sub_type
== XPC_TYPE_ARRAY
) {
235 compound_begin
= "[";
237 xpc_array_apply(object
, ^bool (size_t __unused index
, xpc_object_t value
) {
238 xpc_type_t sub_type
= xpc_get_type(value
);
239 if (sub_type
== XPC_TYPE_DICTIONARY
|| sub_type
== XPC_TYPE_ARRAY
) {
247 const char **keys
= NULL
;
249 char linebuf
[160], *p_space
;
250 size_t space_avail
= sizeof(linebuf
);
253 if (type
== XPC_TYPE_DICTIONARY
) {
254 count
= xpc_dictionary_get_count(object
);
256 count
= xpc_array_get_count(object
);
259 values
= malloc(count
* sizeof(*values
));
260 if (values
== NULL
) {
261 INFO("cti_log_object: no memory");
264 if (type
== XPC_TYPE_DICTIONARY
) {
265 int index
= 0, *p_index
= &index
;
266 keys
= malloc(count
* sizeof(*keys
));
269 INFO("cti_log_object: no memory");
271 xpc_dictionary_apply(object
, ^bool (const char *key
, xpc_object_t value
) {
272 values
[*p_index
] = cti_xpc_copy_description(value
);
273 keys
[*p_index
] = key
;
278 xpc_array_apply(object
, ^bool (size_t index
, xpc_object_t value
) {
279 values
[index
] = cti_xpc_copy_description(value
);
284 for (i
= 0; i
< count
; i
++) {
285 char *str
= values
[i
];
288 bool emitted
= false;
293 len
= strlen(str
) + 2;
295 if (type
== XPC_TYPE_DICTIONARY
) {
296 #ifdef __clang_analyzer__
299 len
+= strlen(keys
[i
]) + 2; // "key: "
302 if (len
+ 1 > space_avail
) {
303 if (i
+ 1 == count
) {
306 if (space_avail
!= sizeof(linebuf
)) {
308 INFO(PUB_S_SRP
"(" PUB_S_SRP
"): " PUB_S_SRP PUB_S_SRP PUB_S_SRP
" " PUB_S_SRP PUB_S_SRP PUB_S_SRP
,
309 context
, command
, indent
, preamble
, divide
, compound_begin
, linebuf
, eol
);
312 INFO(PUB_S_SRP
"(" PUB_S_SRP
"): " PUB_S_SRP PUB_S_SRP PUB_S_SRP
" +" PUB_S_SRP PUB_S_SRP
,
313 context
, command
, indent
, preamble
, divide
, linebuf
, eol
);
315 space_avail
= sizeof linebuf
;
318 if (len
+ 1 > space_avail
) {
319 if (type
== XPC_TYPE_DICTIONARY
) {
320 #ifndef __clang_analyzer__
322 INFO(PUB_S_SRP
"(" PUB_S_SRP
"): " PUB_S_SRP PUB_S_SRP PUB_S_SRP
" " PUB_S_SRP PUB_S_SRP
": " PUB_S_SRP PUB_S_SRP
,
323 context
, command
, indent
, preamble
, divide
, compound_begin
, keys
[i
], str
, eol
);
326 INFO(PUB_S_SRP
"(" PUB_S_SRP
"): " PUB_S_SRP PUB_S_SRP PUB_S_SRP
" +" PUB_S_SRP
": " PUB_S_SRP PUB_S_SRP
,
327 context
, command
, indent
, preamble
, divide
, keys
[i
], str
, eol
);
332 INFO(PUB_S_SRP
"(" PUB_S_SRP
"): " PUB_S_SRP PUB_S_SRP PUB_S_SRP
" " PUB_S_SRP PUB_S_SRP PUB_S_SRP
,
333 context
, command
, indent
, preamble
, divide
, compound_begin
, str
, eol
);
336 INFO(PUB_S_SRP
"(" PUB_S_SRP
"): " PUB_S_SRP PUB_S_SRP PUB_S_SRP
" +" PUB_S_SRP PUB_S_SRP
,
337 context
, command
, indent
, preamble
, divide
, str
, eol
);
344 if (type
== XPC_TYPE_DICTIONARY
) {
345 #ifndef __clang_analyzer__
346 snprintf(p_space
, space_avail
, "%s%s: %s%s", i
== 0 ? "" : " ", keys
[i
], str
, i
+ 1 == count
? "" : ",");
349 snprintf(p_space
, space_avail
, "%s%s%s", i
== 0 ? "" : " ", str
, i
+ 1 == count
? "" : ",");
351 len
= strlen(p_space
);
355 if (values
[i
] != NULL
) {
360 if (linebuf
!= p_space
) {
362 INFO(PUB_S_SRP
"(" PUB_S_SRP
"): " PUB_S_SRP PUB_S_SRP PUB_S_SRP
" " PUB_S_SRP PUB_S_SRP PUB_S_SRP
,
363 context
, command
, indent
, preamble
, divide
, compound_begin
, linebuf
, compound_end
);
365 INFO(PUB_S_SRP
"(" PUB_S_SRP
"): " PUB_S_SRP PUB_S_SRP PUB_S_SRP
" + " PUB_S_SRP PUB_S_SRP
,
366 context
, command
, indent
, preamble
, divide
, linebuf
, compound_end
);
374 depth
= strlen(indent
);
375 new_indent
= malloc(depth
+ 3);
376 if (new_indent
== NULL
) {
379 memset(new_indent
, ' ', depth
+ 2);
380 new_indent
[depth
+ 2] = 0;
382 if (type
== XPC_TYPE_DICTIONARY
) {
383 xpc_dictionary_apply(object
, ^bool (const char *key
, xpc_object_t value
) {
384 cti_log_object(context
, command
, key
, ": ", value
, new_indent
);
388 xpc_array_apply(object
, ^bool (size_t index
, xpc_object_t value
) {
390 snprintf(numbuf
, sizeof(numbuf
), "%zd", index
);
391 cti_log_object(context
, command
, numbuf
, ": ", value
, new_indent
);
395 if (new_indent
!= indent
) {
400 desc
= cti_xpc_copy_description(object
);
401 INFO(PUB_S_SRP
"(" PUB_S_SRP
"): " PUB_S_SRP PUB_S_SRP PUB_S_SRP
" " PUB_S_SRP
,
402 context
, command
, indent
, preamble
, divide
, desc
);
408 cti_event_handler(xpc_object_t event
, cti_connection_t conn_ref
)
410 if (event
== XPC_ERROR_CONNECTION_INVALID
) {
411 INFO("cti_event_handler (" PUB_S_SRP
"): cleanup", conn_ref
->command_name
);
412 if (conn_ref
->callback
.reply
!= NULL
) {
413 conn_ref
->internal_callback(conn_ref
, event
, kCTIStatus_Disconnected
);
417 if (conn_ref
->connection
!= NULL
) {
418 xpc_release(conn_ref
->connection
);
419 conn_ref
->connection
= NULL
;
421 } else if (xpc_get_type(event
) == XPC_TYPE_DICTIONARY
) {
422 cti_log_object("cti_event_handler", conn_ref
->command_name
, "", "", event
, "");
423 if (!conn_ref
->checked_in
) {
424 xpc_object_t command_result
= xpc_dictionary_get_value(event
, "commandResult");
426 if (command_result
!= NULL
) {
427 status
= (int)xpc_int64_get_value(command_result
);
429 xpc_object_t command_data
= xpc_dictionary_get_value(event
, "commandData");
430 if (command_data
== NULL
) {
433 xpc_object_t ret_value
= xpc_dictionary_get_value(command_data
, "ret");
434 if (ret_value
== NULL
) {
437 status
= (int)xpc_int64_get_value(ret_value
);
444 conn_ref
->internal_callback(conn_ref
, event
, kCTIStatus_UnknownError
);
445 xpc_connection_cancel(conn_ref
->connection
);
446 } else if (conn_ref
->property_name
!= NULL
) {
447 // We're meant to both get the property and subscribe to events on it.
448 xpc_object_t
*dict
= xpc_dictionary_create(NULL
, NULL
, 0);
450 ERROR("cti_event_handler(" PUB_S_SRP
"): no memory.", conn_ref
->command_name
);
451 xpc_connection_cancel(conn_ref
->connection
);
453 xpc_object_t
*array
= xpc_array_create(NULL
, 0);
455 ERROR("cti_event_handler(" PUB_S_SRP
"): no memory.", conn_ref
->command_name
);
456 xpc_connection_cancel(conn_ref
->connection
);
458 xpc_dictionary_set_string(dict
, "command", "eventsOn");
459 xpc_dictionary_set_string(dict
, "clientName", "wpanctl");
460 xpc_dictionary_set_value(dict
, "eventList", array
);
461 xpc_array_set_string(array
, XPC_ARRAY_APPEND
, conn_ref
->property_name
);
462 conn_ref
->property_name
= NULL
;
463 cti_log_object("cti_event_handler/events on", conn_ref
->command_name
, "", "", dict
, "");
464 xpc_connection_send_message_with_reply(conn_ref
->connection
, dict
, conn_ref
->client_queue
,
465 ^(xpc_object_t in_event
) {
466 cti_event_handler(in_event
, conn_ref
);
473 xpc_object_t
*message
= conn_ref
->first_command
;
474 conn_ref
->first_command
= NULL
;
475 cti_log_object("cti_event_handler/command is", conn_ref
->command_name
, "", "", message
, "");
476 conn_ref
->checked_in
= true;
478 xpc_connection_send_message_with_reply(conn_ref
->connection
, message
, conn_ref
->client_queue
,
479 ^(xpc_object_t in_event
) {
480 cti_event_handler(in_event
, conn_ref
);
482 xpc_release(message
);
485 conn_ref
->internal_callback(conn_ref
, event
, kCTIStatus_NoError
);
488 cti_log_object("cti_event_handler/other", conn_ref
->command_name
, "", "", event
, "");
489 ERROR("cti_event_handler: Unexpected Connection Error [" PUB_S_SRP
"]",
490 xpc_dictionary_get_string(event
, XPC_ERROR_KEY_DESCRIPTION
));
491 conn_ref
->internal_callback(conn_ref
, NULL
, kCTIStatus_DaemonNotRunning
);
492 if (event
!= XPC_ERROR_CONNECTION_INTERRUPTED
) {
493 xpc_connection_cancel(conn_ref
->connection
);
498 // Creates a new cti_ Connection Reference(cti_connection_t)
500 init_connection(cti_connection_t
*ref
, const char *servname
, xpc_object_t
*dict
, const char *command_name
,
501 const char *property_name
, void *context
, cti_callback_t app_callback
,
502 cti_internal_callback_t internal_callback
, dispatch_queue_t client_queue
, const char *file
, int line
)
504 // Use an cti_connection_t on the stack to be captured in the blocks below, rather than
505 // capturing the cti_connection_t* owned by the client
506 cti_connection_t conn_ref
= calloc(1, sizeof(struct _cti_connection_t
));
507 if (conn_ref
== NULL
) {
508 ERROR("dns_services: init_connection() No memory to allocate!");
509 return kCTIStatus_NoMemory
;
512 // Initialize the cti_connection_t
513 dispatch_retain(client_queue
);
514 conn_ref
->command_name
= command_name
;
515 conn_ref
->property_name
= property_name
;
516 conn_ref
->context
= context
;
517 conn_ref
->client_queue
= client_queue
;
518 conn_ref
->callback
= app_callback
;
519 conn_ref
->internal_callback
= internal_callback
;
520 conn_ref
->connection
= xpc_connection_create_mach_service(servname
, conn_ref
->client_queue
,
521 XPC_CONNECTION_MACH_SERVICE_PRIVILEGED
);
522 conn_ref
->first_command
= dict
;
525 cti_log_object("init_connection/command", conn_ref
->command_name
, "", "", dict
, "");
527 if (conn_ref
->connection
== NULL
)
529 ERROR("dns_services: init_connection() conn_ref/lib_q is NULL");
530 if (conn_ref
!= NULL
) {
533 return kCTIStatus_NoMemory
;
536 RETAIN_HERE(conn_ref
); // For the event handler.
537 xpc_connection_set_event_handler(conn_ref
->connection
, ^(xpc_object_t event
) { cti_event_handler(event
, conn_ref
); });
538 xpc_connection_set_finalizer_f(conn_ref
->connection
, cti_xpc_connection_finalize
);
539 xpc_connection_set_context(conn_ref
->connection
, conn_ref
);
540 xpc_connection_resume(conn_ref
->connection
);
542 char srp_name
[] = "srp-mdns-proxyd";
543 char client_name
[sizeof(srp_name
) + 20];
544 snprintf(client_name
, sizeof client_name
, "%s-%d", srp_name
, client_serial_number
);
545 client_serial_number
++;
547 xpc_object_t checkin_command
= xpc_dictionary_create(NULL
, NULL
, 0);
549 xpc_dictionary_set_string(checkin_command
, "command", "checkIn");
550 xpc_dictionary_set_string(checkin_command
, "clientName", client_name
);
552 cti_log_object("init_connection/checkin", conn_ref
->command_name
, "", "", checkin_command
, "");
553 xpc_connection_send_message_with_reply(conn_ref
->connection
, checkin_command
, conn_ref
->client_queue
,
554 ^(xpc_object_t event
) { cti_event_handler(event
, conn_ref
); });
556 xpc_release(checkin_command
);
560 // We always retain a reference for the caller, even if the caller doesn't actually hold the reference.
561 // Calls that do not result in repeated callbacks release this reference after calling the callback.
562 // Such calls do not return a reference to the caller, so there is no chance of a double release.
563 // Calls that result in repeated callbacks have to release the reference by calling cti_events_discontinue.
564 // If this isn't done, the reference will never be released.
567 return kCTIStatus_NoError
;
570 #define setup_for_command(ref, client_queue, command_name, property_name, dict, command, \
571 context, app_callback, internal_callback) \
572 setup_for_command_(ref, client_queue, command_name, property_name, dict, command, \
573 context, app_callback, internal_callback, __FILE__, __LINE__)
575 setup_for_command_(cti_connection_t
*ref
, dispatch_queue_t client_queue
, const char *command_name
,
576 const char *property_name
, xpc_object_t dict
, const char *command
, void *context
,
577 cti_callback_t app_callback
, cti_internal_callback_t internal_callback
, const char *file
, int line
)
579 cti_status_t errx
= kCTIStatus_NoError
;
582 if (app_callback
.reply
== NULL
|| internal_callback
== NULL
|| client_queue
== NULL
)
584 ERROR(PUB_S_SRP
": NULL cti_connection_t OR Callback OR Client_Queue parameter", command_name
);
585 return kCTIStatus_BadParam
;
588 // Get conn_ref from init_connection()
589 xpc_dictionary_set_string(dict
, "command", command
);
591 errx
= init_connection(ref
, "com.apple.wpantund.xpc", dict
, command_name
, property_name
,
592 context
, app_callback
, internal_callback
, client_queue
, file
, line
);
593 if (errx
) // On error init_connection() leaves *conn_ref set to NULL
595 ERROR(PUB_S_SRP
": Since init_connection() returned %d error returning w/o sending msg", command_name
, errx
);
603 cti_internal_reply_callback(cti_connection_t NONNULL conn_ref
, xpc_object_t __unused reply
, cti_status_t status
)
605 cti_reply_t callback
;
606 INFO("cti_internal_reply_callback: conn_ref = %p", conn_ref
);
607 callback
= conn_ref
->callback
.reply
;
608 if (callback
!= NULL
) {
609 callback(conn_ref
->context
, status
);
611 cti_connection_release(conn_ref
);
615 cti_add_service(void *context
, cti_reply_t callback
, dispatch_queue_t client_queue
,
616 uint32_t enterprise_number
, const uint8_t *NONNULL service_data
, size_t service_data_length
,
617 const uint8_t *NONNULL server_data
, size_t server_data_length
)
620 xpc_object_t dict
= xpc_dictionary_create(NULL
, NULL
, 0);
622 cti_callback_t app_callback
;
623 app_callback
.reply
= callback
;
625 xpc_dictionary_set_data(dict
, "service_data", service_data
, service_data_length
);
626 xpc_dictionary_set_data(dict
, "server_data", server_data
, server_data_length
);
627 xpc_dictionary_set_uint64(dict
, "enterprise_number", enterprise_number
);
628 xpc_dictionary_set_string(dict
, "interface", "org.wpantund.v1");
629 xpc_dictionary_set_string(dict
, "path", "/org/wpantund/utun2");
630 xpc_dictionary_set_string(dict
, "method", "ServiceAdd");
631 xpc_dictionary_set_bool(dict
, "stable", true);
633 errx
= setup_for_command(NULL
, client_queue
, "add_service", NULL
, dict
, "WpanctlCmd",
634 context
, app_callback
, cti_internal_reply_callback
);
641 cti_remove_service(void *context
, cti_reply_t callback
, dispatch_queue_t client_queue
,
642 uint32_t enterprise_number
, const uint8_t *NONNULL service_data
, size_t service_data_length
)
645 xpc_object_t dict
= xpc_dictionary_create(NULL
, NULL
, 0);
647 cti_callback_t app_callback
;
648 app_callback
.reply
= callback
;
650 xpc_dictionary_set_data(dict
, "service_data", service_data
, service_data_length
);
651 xpc_dictionary_set_uint64(dict
, "enterprise_number", enterprise_number
);
652 xpc_dictionary_set_string(dict
, "interface", "org.wpantund.v1");
653 xpc_dictionary_set_string(dict
, "path", "/org/wpantund/utun2");
654 xpc_dictionary_set_string(dict
, "method", "ServiceRemove");
656 errx
= setup_for_command(NULL
, client_queue
, "remove_service", NULL
, dict
, "WpanctlCmd",
657 context
, app_callback
, cti_internal_reply_callback
);
664 cti_do_prefix(void *context
, cti_reply_t callback
, dispatch_queue_t client_queue
,
665 struct in6_addr
*prefix
, int prefix_length
, bool on_mesh
, bool preferred
, bool slaac
, bool stable
, bool adding
)
668 xpc_object_t dict
= xpc_dictionary_create(NULL
, NULL
, 0);
670 cti_callback_t app_callback
;
671 app_callback
.reply
= callback
;
674 ERROR("cti_do_prefix: no memory for command dictionary.");
675 return kCTIStatus_NoMemory
;
677 xpc_dictionary_set_bool(dict
, "preferred", preferred
);
679 xpc_dictionary_set_uint64(dict
, "preferredLifetime", ND6_INFINITE_LIFETIME
);
680 xpc_dictionary_set_uint64(dict
, "validLifetime", ND6_INFINITE_LIFETIME
);
682 xpc_dictionary_set_uint64(dict
, "preferredLifetime", 0);
683 xpc_dictionary_set_uint64(dict
, "validLifetime", 0);
685 xpc_dictionary_set_int64(dict
, "prefix_length", 16);
686 xpc_dictionary_set_bool(dict
, "dhcp", false);
687 xpc_dictionary_set_data(dict
, "prefix", prefix
, sizeof(*prefix
));
688 xpc_dictionary_set_string(dict
, "interface", "org.wpantund.v1");
689 xpc_dictionary_set_uint64(dict
, "prefix_len_in_bits", prefix_length
);
690 xpc_dictionary_set_bool(dict
, "slaac", slaac
);
691 xpc_dictionary_set_bool(dict
, "onMesh", on_mesh
);
692 xpc_dictionary_set_bool(dict
, "configure", false);
693 xpc_dictionary_set_string(dict
, "path", "/org/wpantund/utun2");
694 xpc_dictionary_set_string(dict
, "method", "ConfigGateway");
695 xpc_dictionary_set_bool(dict
, "stable", stable
);
696 xpc_dictionary_set_bool(dict
, "defaultRoute", adding
);
697 xpc_dictionary_set_int64(dict
, "priority", 0);
699 errx
= setup_for_command(NULL
, client_queue
, "add_prefix", NULL
, dict
, "WpanctlCmd",
700 context
, app_callback
, cti_internal_reply_callback
);
707 cti_add_prefix(void *context
, cti_reply_t callback
, dispatch_queue_t client_queue
,
708 struct in6_addr
*prefix
, int prefix_length
, bool on_mesh
, bool preferred
, bool slaac
, bool stable
)
710 return cti_do_prefix(context
, callback
, client_queue
, prefix
, prefix_length
, on_mesh
, preferred
, slaac
, stable
, true);
714 cti_remove_prefix(void *NULLABLE context
, cti_reply_t NONNULL callback
, dispatch_queue_t NONNULL client_queue
,
715 struct in6_addr
*NONNULL prefix
, int prefix_length
)
718 return cti_do_prefix(context
, callback
, client_queue
, prefix
, prefix_length
, false, false, false, false, false);
722 cti_internal_tunnel_reply_callback(cti_connection_t NONNULL conn_ref
, xpc_object_t reply
, cti_status_t status_in
)
724 cti_tunnel_reply_t callback
= conn_ref
->callback
.tunnel_reply
;
726 cti_status_t status
= status_in
;
727 const char *tunnel_name
= NULL
;
728 uint64_t command_result
= xpc_dictionary_get_int64(reply
, "commandResult");
729 if (command_result
!= 0) {
730 ERROR("cti_internal_tunnel_reply_callback: nonzero result %llu", command_result
);
731 status
= kCTIStatus_UnknownError
;
733 xpc_object_t result_dictionary
= xpc_dictionary_get_dictionary(reply
, "commandData");
734 if (status
== kCTIStatus_NoError
) {
735 if (result_dictionary
!= NULL
) {
736 const char *property_name
= xpc_dictionary_get_string(result_dictionary
, "property_name");
737 if (property_name
== NULL
|| strcmp(property_name
, "Config:TUN:InterfaceName")) {
738 status
= kCTIStatus_UnknownError
;
740 tunnel_name
= xpc_dictionary_get_string(result_dictionary
, "value");
741 if (tunnel_name
== NULL
) {
742 status
= kCTIStatus_UnknownError
;
746 status
= kCTIStatus_UnknownError
;
750 if (callback
!= NULL
) {
751 callback(conn_ref
->context
, tunnel_name
, status
);
754 conn_ref
->callback
.reply
= NULL
;
755 if (conn_ref
->connection
!= NULL
) {
756 xpc_connection_cancel(conn_ref
->connection
);
761 cti_get_tunnel_name(void *NULLABLE context
, cti_tunnel_reply_t NONNULL callback
, dispatch_queue_t NONNULL client_queue
)
764 xpc_object_t dict
= xpc_dictionary_create(NULL
, NULL
, 0);
766 cti_callback_t app_callback
;
767 app_callback
.tunnel_reply
= callback
;
769 xpc_dictionary_set_string(dict
, "interface", "org.wpantund.v1");
770 xpc_dictionary_set_string(dict
, "path", "/org/wpantund/utun2");
771 xpc_dictionary_set_string(dict
, "method", "PropGet");
772 xpc_dictionary_set_string(dict
, "property_name", "Config:TUN:InterfaceName");
774 errx
= setup_for_command(NULL
, client_queue
, "get_tunnel_name", NULL
, dict
, "WpanctlCmd",
775 context
, app_callback
, cti_internal_tunnel_reply_callback
);
782 cti_event_or_response_extract(xpc_object_t
*reply
, xpc_object_t
*result_dictionary
)
784 xpc_object_t
*result
= xpc_dictionary_get_dictionary(reply
, "commandData");
785 if (result
== NULL
) {
786 result
= xpc_dictionary_get_dictionary(reply
, "eventData");
788 int command_status
= (int)xpc_dictionary_get_int64(reply
, "commandResult");
789 if (command_status
!= 0) {
790 INFO("cti_event_or_response_extract: nonzero status %d", command_status
);
791 return kCTIStatus_UnknownError
;
794 if (result
!= NULL
) {
795 *result_dictionary
= result
;
796 return kCTIStatus_NoError
;
798 INFO("cti_event_or_response_extract: null result");
799 return kCTIStatus_UnknownError
;
803 cti_internal_state_reply_callback(cti_connection_t NONNULL conn_ref
, xpc_object_t reply
, cti_status_t status_in
)
805 cti_state_reply_t callback
= conn_ref
->callback
.state_reply
;
806 cti_network_state_t state
= kCTI_NCPState_Unknown
;
807 cti_status_t status
= status_in
;
808 if (status
== kCTIStatus_NoError
) {
809 xpc_object_t result_dictionary
= NULL
;
810 status
= cti_event_or_response_extract(reply
, &result_dictionary
);
811 if (status
== kCTIStatus_NoError
) {
812 const char *state_name
= xpc_dictionary_get_string(result_dictionary
, "value");
813 if (state_name
== NULL
) {
814 status
= kCTIStatus_UnknownError
;
815 } else if (!strcmp(state_name
, "uninitialized")) {
816 state
= kCTI_NCPState_Uninitialized
;
817 } else if (!strcmp(state_name
, "uninitialized:fault")) {
818 state
= kCTI_NCPState_Fault
;
819 } else if (!strcmp(state_name
, "uninitialized:upgrading")) {
820 state
= kCTI_NCPState_Upgrading
;
821 } else if (!strcmp(state_name
, "offline:deep-sleep")) {
822 state
= kCTI_NCPState_DeepSleep
;
823 } else if (!strcmp(state_name
, "offline")) {
824 state
= kCTI_NCPState_Offline
;
825 } else if (!strcmp(state_name
, "offline:commissioned")) {
826 state
= kCTI_NCPState_Commissioned
;
827 } else if (!strcmp(state_name
, "associating")) {
828 state
= kCTI_NCPState_Associating
;
829 } else if (!strcmp(state_name
, "associating:credentials-needed")) {
830 state
= kCTI_NCPState_CredentialsNeeded
;
831 } else if (!strcmp(state_name
, "associated")) {
832 state
= kCTI_NCPState_Associated
;
833 } else if (!strcmp(state_name
, "associated:no-parent")) {
834 state
= kCTI_NCPState_Isolated
;
835 } else if (!strcmp(state_name
, "associated:netwake-asleep")) {
836 state
= kCTI_NCPState_NetWake_Asleep
;
837 } else if (!strcmp(state_name
, "associated:netwake-waking")) {
838 state
= kCTI_NCPState_NetWake_Waking
;
842 if (callback
!= NULL
) {
843 callback(conn_ref
->context
, state
, status
);
848 cti_get_state(cti_connection_t
*ref
, void *NULLABLE context
, cti_state_reply_t NONNULL callback
,
849 dispatch_queue_t NONNULL client_queue
)
852 xpc_object_t dict
= xpc_dictionary_create(NULL
, NULL
, 0);
854 cti_callback_t app_callback
;
855 app_callback
.state_reply
= callback
;
857 xpc_dictionary_set_string(dict
, "interface", "org.wpantund.v1");
858 xpc_dictionary_set_string(dict
, "path", "/org/wpantund/utun2");
859 xpc_dictionary_set_string(dict
, "method", "PropGet");
860 xpc_dictionary_set_string(dict
, "property_name", "NCP:State");
862 errx
= setup_for_command(ref
, client_queue
, "get_state", "NCP:State", dict
, "WpanctlCmd",
863 context
, app_callback
, cti_internal_state_reply_callback
);
870 cti_internal_partition_id_callback(cti_connection_t NONNULL conn_ref
, xpc_object_t reply
, cti_status_t status_in
)
872 cti_partition_id_reply_t callback
= conn_ref
->callback
.partition_id_reply
;
873 int32_t partition_id
= -1;
874 cti_status_t status
= status_in
;
875 if (status
== kCTIStatus_NoError
) {
876 xpc_object_t result_dictionary
= NULL
;
877 status
= cti_event_or_response_extract(reply
, &result_dictionary
);
878 if (status
== kCTIStatus_NoError
) {
879 xpc_object_t value
= xpc_dictionary_get_value(result_dictionary
, "value");
881 ERROR("cti_internal_partition_id_callback: No partition ID returned.");
882 } else if (xpc_get_type(value
) != XPC_TYPE_UINT64
) {
883 char *value_string
= xpc_copy_description(value
);
884 ERROR("cti_internal_partition_id_callback: Partition ID is " PUB_S_SRP
" instead if uint64_t.",
888 partition_id
= (int32_t)xpc_dictionary_get_uint64(result_dictionary
, "value");
892 if (callback
!= NULL
) {
893 callback(conn_ref
->context
, partition_id
, status
);
898 cti_get_partition_id(cti_connection_t
*ref
, void *NULLABLE context
, cti_partition_id_reply_t NONNULL callback
,
899 dispatch_queue_t NONNULL client_queue
)
902 xpc_object_t dict
= xpc_dictionary_create(NULL
, NULL
, 0);
904 cti_callback_t app_callback
;
905 app_callback
.partition_id_reply
= callback
;
907 xpc_dictionary_set_string(dict
, "interface", "org.wpantund.v1");
908 xpc_dictionary_set_string(dict
, "path", "/org/wpantund/utun2");
909 xpc_dictionary_set_string(dict
, "method", "PropGet");
910 xpc_dictionary_set_string(dict
, "property_name", "Network:PartitionId");
912 errx
= setup_for_command(ref
, client_queue
, "get_partition_id", "Network:PartitionId", dict
, "WpanctlCmd",
913 context
, app_callback
, cti_internal_partition_id_callback
);
920 cti_internal_network_node_type_callback(cti_connection_t NONNULL conn_ref
, xpc_object_t reply
, cti_status_t status_in
)
922 cti_network_node_type_reply_t callback
= conn_ref
->callback
.network_node_type_reply
;
923 cti_network_node_type_t network_node_type
= kCTI_NetworkNodeType_Unknown
;
924 cti_status_t status
= status_in
;
925 if (status
== kCTIStatus_NoError
) {
926 xpc_object_t result_dictionary
= NULL
;
927 status
= cti_event_or_response_extract(reply
, &result_dictionary
);
928 if (status
== kCTIStatus_NoError
) {
929 xpc_object_t value
= xpc_dictionary_get_value(result_dictionary
, "value");
931 ERROR("cti_internal_network_node_type_callback: No node type returned.");
932 } else if (xpc_get_type(value
) != XPC_TYPE_STRING
) {
933 char *value_string
= xpc_copy_description(value
);
934 ERROR("cti_internal_network_node_type_callback: node type type is " PUB_S_SRP
" instead of string.",
938 const char *node_type_name
= xpc_dictionary_get_string(result_dictionary
, "value");
939 if (!strcmp(node_type_name
, "unknown")) {
940 network_node_type
= kCTI_NetworkNodeType_Unknown
;
941 } else if (!strcmp(node_type_name
, "router")) {
942 network_node_type
= kCTI_NetworkNodeType_Router
;
943 } else if (!strcmp(node_type_name
, "end-device")) {
944 network_node_type
= kCTI_NetworkNodeType_EndDevice
;
945 } else if (!strcmp(node_type_name
, "sleepy-end-device")) {
946 network_node_type
= kCTI_NetworkNodeType_SleepyEndDevice
;
947 } else if (!strcmp(node_type_name
, "nl-lurker")) {
948 network_node_type
= kCTI_NetworkNodeType_NestLurker
;
949 } else if (!strcmp(node_type_name
, "commissioner")) {
950 network_node_type
= kCTI_NetworkNodeType_Commissioner
;
951 } else if (!strcmp(node_type_name
, "leader")) {
952 network_node_type
= kCTI_NetworkNodeType_Leader
;
957 if (callback
!= NULL
) {
958 callback(conn_ref
->context
, network_node_type
, status
);
963 cti_get_network_node_type(cti_connection_t
*ref
, void *NULLABLE context
, cti_network_node_type_reply_t NONNULL callback
,
964 dispatch_queue_t NONNULL client_queue
)
967 xpc_object_t dict
= xpc_dictionary_create(NULL
, NULL
, 0);
969 cti_callback_t app_callback
;
970 app_callback
.network_node_type_reply
= callback
;
972 xpc_dictionary_set_string(dict
, "interface", "org.wpantund.v1");
973 xpc_dictionary_set_string(dict
, "path", "/org/wpantund/utun2");
974 xpc_dictionary_set_string(dict
, "method", "PropGet");
975 xpc_dictionary_set_string(dict
, "property_name", "Network:NodeType");
977 errx
= setup_for_command(ref
, client_queue
, "get_network_node_type", "Network:NodeType", dict
, "WpanctlCmd",
978 context
, app_callback
, cti_internal_network_node_type_callback
);
985 cti_service_finalize(cti_service_t
*service
)
987 if (service
->server
!= NULL
) {
988 free(service
->server
);
994 cti_service_vec_finalize(cti_service_vec_t
*services
)
998 if (services
->services
!= NULL
) {
999 for (i
= 0; i
< services
->num
; i
++) {
1000 if (services
->services
[i
] != NULL
) {
1001 RELEASE_HERE(services
->services
[i
], cti_service_finalize
);
1004 free(services
->services
);
1010 cti_service_vec_create_(size_t num_services
, const char *file
, int line
)
1012 cti_service_vec_t
*services
= calloc(1, sizeof(*services
));
1013 if (services
!= NULL
) {
1014 if (num_services
!= 0) {
1015 services
->services
= calloc(num_services
, sizeof(cti_service_t
*));
1016 if (services
->services
== NULL
) {
1021 services
->num
= num_services
;
1028 cti_service_vec_release_(cti_service_vec_t
*services
, const char *file
, int line
)
1030 RELEASE(services
, cti_service_vec_finalize
);
1034 cti_service_create_(uint64_t enterprise_number
, uint16_t service_type
, uint16_t service_version
,
1035 uint8_t *server
, size_t server_length
, int flags
, const char *file
, int line
)
1037 cti_service_t
*service
= calloc(1, sizeof(*service
));
1038 if (service
!= NULL
) {
1039 service
->enterprise_number
= enterprise_number
;
1040 service
->service_type
= service_type
;
1041 service
->service_version
= service_version
;
1042 service
->server
= server
;
1043 service
->server_length
= server_length
;
1044 service
->flags
= flags
;
1051 cti_service_release_(cti_service_t
*service
, const char *file
, int line
)
1053 RELEASE(service
, cti_service_finalize
);
1057 cti_array_to_bytes(xpc_object_t array
, size_t *length_ret
, const char *log_name
)
1059 size_t length
= xpc_array_get_count(array
);
1063 ret
= malloc(length
);
1065 ERROR(PUB_S_SRP
": no memory for return buffer", log_name
);
1069 for (i
= 0; i
< length
; i
++) {
1070 uint64_t v
= xpc_array_get_uint64(array
, i
);
1073 *length_ret
= length
;
1078 cti_parse_services_array(cti_service_vec_t
**services
, xpc_object_t services_array
)
1080 size_t services_array_length
= xpc_array_get_count(services_array
);
1082 cti_service_vec_t
*service_vec
;
1083 cti_service_t
*service
;
1084 cti_status_t status
= kCTIStatus_NoError
;
1086 service_vec
= cti_service_vec_create(services_array_length
);
1087 if (service_vec
== NULL
) {
1088 return kCTIStatus_NoMemory
;
1092 for (i
= 0; i
< services_array_length
; i
++) {
1093 xpc_object_t service_array
= xpc_array_get_value(services_array
, i
);
1094 int match_count
= 0;
1095 bool matched
[5] = { false, false, false, false, false};
1096 uint64_t enterprise_number
= 0;
1097 uint8_t *server_data
= NULL
;
1098 size_t server_data_length
= 0;
1099 uint8_t *service_data
= NULL
;
1100 size_t service_data_length
= 0;
1103 if (service_array
== NULL
) {
1104 ERROR("Unable to get service array %zd", i
);
1106 size_t service_array_length
= xpc_array_get_count(service_array
);
1107 for (j
= 0; j
< service_array_length
; j
++) {
1108 xpc_object_t
*array_sub_dict
= xpc_array_get_value(service_array
, j
);
1109 if (array_sub_dict
== NULL
) {
1110 ERROR("can't get service_array %zd subdictionary %zd", i
, j
);
1111 goto service_array_element_failed
;
1113 const char *key
= xpc_dictionary_get_string(array_sub_dict
, "key");
1115 ERROR("Invalid services array %zd subdictionary %zd: no key", i
, j
);
1116 goto service_array_element_failed
;
1117 } else if (!strcmp(key
, "EnterpriseNumber")) {
1119 ERROR("services array %zd: Enterprise number appears twice.", i
);
1120 goto service_array_element_failed
;
1122 enterprise_number
= xpc_dictionary_get_uint64(array_sub_dict
, "value");
1124 } else if (!strcmp(key
, "Origin")) {
1126 ERROR("Services array %zd: Origin appears twice.", i
);
1127 goto service_array_element_failed
;
1129 const char *origin_string
= xpc_dictionary_get_string(array_sub_dict
, "value");
1130 if (origin_string
== NULL
) {
1131 ERROR("Unable to get origin string from services array %zd", i
);
1132 goto service_array_element_failed
;
1133 } else if (!strcmp(origin_string
, "user")) {
1135 } else if (!strcmp(origin_string
, "ncp")) {
1136 flags
|= kCTIFlag_NCP
;
1138 ERROR("unknown origin " PUB_S_SRP
, origin_string
);
1139 goto service_array_element_failed
;
1142 } else if (!strcmp(key
, "ServerData")) {
1144 ERROR("Services array %zd: Server data appears twice.", i
);
1145 goto service_array_element_failed
;
1147 server_data
= cti_array_to_bytes(xpc_dictionary_get_array(array_sub_dict
, "value"),
1148 &server_data_length
, "Server data");
1149 if (server_data
== NULL
) {
1150 goto service_array_element_failed
;
1153 } else if (!strcmp(key
, "ServiceData")) {
1155 ERROR("Services array %zd: Service data appears twice.", i
);
1156 goto service_array_element_failed
;
1158 service_data
= cti_array_to_bytes(xpc_dictionary_get_array(array_sub_dict
, "value"),
1159 &service_data_length
, "Service data");
1160 if (service_data
== NULL
) {
1161 goto service_array_element_failed
;
1164 } else if (!strcmp(key
, "Stable")) {
1166 ERROR("Services array %zd: Stable state appears twice.", i
);
1167 goto service_array_element_failed
;
1169 if (xpc_dictionary_get_bool(array_sub_dict
, "value")) {
1170 flags
|= kCTIFlag_Stable
;
1174 ERROR("Unknown key in service array %zd subdictionary %zd: " PUB_S_SRP
, i
, j
, key
);
1175 goto service_array_element_failed
;
1180 if (match_count
!= 5) {
1181 ERROR("expecting %d sub-dictionaries to service array %zd, but got %d.",
1183 goto service_array_element_failed
;
1185 uint16_t service_type
, service_version
;
1186 if (enterprise_number
== THREAD_ENTERPRISE_NUMBER
) {
1187 if (service_data_length
!= 1) {
1188 INFO("Invalid service data: length = %zd", service_data_length
);
1189 goto service_array_element_failed
;
1191 service_type
= service_data
[0];
1192 service_version
= 1;
1194 // We don't support any other enterprise numbers.
1195 service_type
= service_version
= 0;
1198 service
= cti_service_create(enterprise_number
, service_type
, service_version
,
1199 server_data
, server_data_length
, flags
);
1200 if (service
== NULL
) {
1201 ERROR("Unable to store service %lld %d %d: out of memory.", enterprise_number
,
1202 service_type
, service_version
);
1205 service_vec
->services
[i
] = service
;
1207 goto done_with_service_array
;
1208 service_array_element_failed
:
1209 if (status
== kCTIStatus_NoError
) {
1210 status
= kCTIStatus_UnknownError
;
1212 done_with_service_array
:
1213 if (server_data
!= NULL
) {
1216 if (service_data
!= NULL
) {
1221 if (status
== kCTIStatus_NoError
) {
1222 *services
= service_vec
;
1224 if (service_vec
!= NULL
) {
1225 RELEASE_HERE(service_vec
, cti_service_vec_finalize
);
1232 cti_internal_service_reply_callback(cti_connection_t NONNULL conn_ref
, xpc_object_t reply
, cti_status_t status_in
)
1234 cti_service_reply_t callback
= conn_ref
->callback
.service_reply
;
1235 cti_service_vec_t
*vec
= NULL
;
1236 cti_status_t status
= status_in
;
1237 if (status
== kCTIStatus_NoError
) {
1238 xpc_object_t result_dictionary
= NULL
;
1239 status
= cti_event_or_response_extract(reply
, &result_dictionary
);
1240 if (status
== kCTIStatus_NoError
) {
1241 xpc_object_t
*value
= xpc_dictionary_get_array(result_dictionary
, "value");
1242 if (value
== NULL
) {
1243 INFO("cti_internal_service_reply_callback: services array not present in Thread:Services event.");
1245 status
= cti_parse_services_array(&vec
, value
);
1249 if (callback
!= NULL
) {
1250 callback(conn_ref
->context
, vec
, status
);
1253 RELEASE_HERE(vec
, cti_service_vec_finalize
);
1258 cti_get_service_list(cti_connection_t
*ref
, void *NULLABLE context
, cti_service_reply_t NONNULL callback
,
1259 dispatch_queue_t NONNULL client_queue
)
1262 xpc_object_t dict
= xpc_dictionary_create(NULL
, NULL
, 0);
1264 cti_callback_t app_callback
;
1265 app_callback
.service_reply
= callback
;
1267 xpc_dictionary_set_string(dict
, "interface", "org.wpantund.v1");
1268 xpc_dictionary_set_string(dict
, "path", "/org/wpantund/utun2");
1269 xpc_dictionary_set_string(dict
, "method", "PropGet");
1270 xpc_dictionary_set_string(dict
, "property_name", "Thread:Services");
1272 errx
= setup_for_command(ref
, client_queue
, "get_service_list", "Thread:Services", dict
, "WpanctlCmd",
1273 context
, app_callback
, cti_internal_service_reply_callback
);
1280 cti_prefix_finalize(cti_prefix_t
*prefix
)
1286 cti_prefix_vec_finalize(cti_prefix_vec_t
*prefixes
)
1290 if (prefixes
->prefixes
!= NULL
) {
1291 for (i
= 0; i
< prefixes
->num
; i
++) {
1292 if (prefixes
->prefixes
[i
] != NULL
) {
1293 RELEASE_HERE(prefixes
->prefixes
[i
], cti_prefix_finalize
);
1296 free(prefixes
->prefixes
);
1302 cti_prefix_vec_create_(size_t num_prefixes
, const char *file
, int line
)
1304 cti_prefix_vec_t
*prefixes
= calloc(1, sizeof(*prefixes
));
1305 if (prefixes
!= NULL
) {
1306 if (num_prefixes
!= 0) {
1307 prefixes
->prefixes
= calloc(num_prefixes
, sizeof(cti_prefix_t
*));
1308 if (prefixes
->prefixes
== NULL
) {
1313 prefixes
->num
= num_prefixes
;
1320 cti_prefix_vec_release_(cti_prefix_vec_t
*prefixes
, const char *file
, int line
)
1322 RELEASE(prefixes
, cti_prefix_vec_finalize
);
1326 cti_prefix_create_(struct in6_addr
*prefix
, int prefix_length
, int metric
, int flags
, const char *file
, int line
)
1328 cti_prefix_t
*prefix_ret
= calloc(1, sizeof(*prefix_ret
));
1329 if (prefix
!= NULL
) {
1330 prefix_ret
->prefix
= *prefix
;
1331 prefix_ret
->prefix_length
= prefix_length
;
1332 prefix_ret
->metric
= metric
;
1333 prefix_ret
->flags
= flags
;
1340 cti_prefix_release_(cti_prefix_t
*prefix
, const char *file
, int line
)
1342 RELEASE(prefix
, cti_prefix_finalize
);
1346 cti_parse_prefixes_array(cti_prefix_vec_t
**vec_ret
, xpc_object_t prefixes_array
)
1348 size_t prefixes_array_length
= xpc_array_get_count(prefixes_array
);
1350 cti_prefix_vec_t
*prefixes
= cti_prefix_vec_create(prefixes_array_length
);
1351 cti_status_t status
= kCTIStatus_NoError
;
1353 if (prefixes
== NULL
) {
1354 INFO("cti_parse_prefixes_array: no memory.");
1355 status
= kCTIStatus_NoMemory
;
1358 for (i
= 0; i
< prefixes_array_length
; i
++) {
1359 xpc_object_t prefix_array
= xpc_array_get_value(prefixes_array
, i
);
1360 int match_count
= 0;
1361 bool matched
[5] = { false, false};
1362 const char *destination
= NULL
;
1364 struct in6_addr prefix_addr
;
1366 if (prefix_array
== NULL
) {
1367 ERROR("Unable to get prefix array %zu", i
);
1369 size_t prefix_array_length
= xpc_array_get_count(prefix_array
);
1370 for (j
= 0; j
< prefix_array_length
; j
++) {
1371 xpc_object_t
*array_sub_dict
= xpc_array_get_value(prefix_array
, j
);
1372 if (array_sub_dict
== NULL
) {
1373 ERROR("can't get prefix_array %zu subdictionary %zu", i
, j
);
1374 goto done_with_prefix_array
;
1376 const char *key
= xpc_dictionary_get_string(array_sub_dict
, "key");
1378 ERROR("Invalid prefixes array %zu subdictionary %zu: no key", i
, j
);
1379 goto done_with_prefix_array
;
1381 // Fix me: when <rdar://problem/59371674> is fixed, remove Addreess key test.
1382 else if (!strcmp(key
, "Addreess") || !strcmp(key
, "Address")) {
1384 ERROR("prefixes array %zu: Address appears twice.", i
);
1385 goto done_with_prefix_array
;
1387 destination
= xpc_dictionary_get_string(array_sub_dict
, "value");
1388 if (destination
== NULL
) {
1389 INFO("process_prefixes_array: null address");
1390 goto done_with_prefix_array
;
1393 } else if (!strcmp(key
, "Metric")) {
1395 ERROR("prefixes array %zu: Metric appears twice.", i
);
1396 goto done_with_prefix_array
;
1398 metric
= (int)xpc_dictionary_get_uint64(array_sub_dict
, "value");
1400 ERROR("Unknown key in prefix array %zu subdictionary %zu: " PUB_S_SRP
, i
, j
, key
);
1401 goto done_with_prefix_array
;
1406 if (match_count
!= 2) {
1407 ERROR("expecting %d sub-dictionaries to prefix array %zu, but got %d.",
1409 goto done_with_prefix_array
;
1412 // The prefix is in IPv6 address presentation form, so convert it to bits.
1413 char prefix_buffer
[INET6_ADDRSTRLEN
];
1414 const char *slash
= strchr(destination
, '/');
1415 size_t prefix_pres_len
= slash
- destination
;
1416 if (prefix_pres_len
>= INET6_ADDRSTRLEN
- 1) {
1417 ERROR("prefixes array %zu: destination is longer than maximum IPv6 address string: " PUB_S_SRP
,
1419 goto done_with_prefix_array
;
1421 #ifndef __clang_analyzer__ // destination is never null at this point
1422 memcpy(prefix_buffer
, destination
, prefix_pres_len
);
1424 prefix_buffer
[prefix_pres_len
] = 0;
1425 inet_pton(AF_INET6
, prefix_buffer
, &prefix_addr
);
1427 // Also convert the prefix.
1428 char *endptr
= NULL
;
1429 int prefix_len
= (int)strtol(slash
+ 1, &endptr
, 10);
1430 if (endptr
== slash
+ 1 || *endptr
!= 0 || prefix_len
!= 64) {
1431 INFO("bogus prefix length provided by thread: " PUB_S_SRP
, destination
);
1435 cti_prefix_t
*prefix
= cti_prefix_create(&prefix_addr
, prefix_len
, metric
, 0);
1436 if (prefix
!= NULL
) {
1437 prefixes
->prefixes
[i
] = prefix
;
1440 done_with_prefix_array
:
1441 status
= kCTIStatus_UnknownError
;
1445 if (status
== kCTIStatus_NoError
) {
1446 *vec_ret
= prefixes
;
1448 if (prefixes
!= NULL
) {
1449 RELEASE_HERE(prefixes
, cti_prefix_vec_finalize
);
1456 cti_internal_prefix_reply_callback(cti_connection_t NONNULL conn_ref
, xpc_object_t reply
, cti_status_t status_in
)
1458 cti_prefix_reply_t callback
= conn_ref
->callback
.prefix_reply
;
1459 cti_status_t status
= status_in
;
1460 cti_prefix_vec_t
*vec
= NULL
;
1461 xpc_object_t result_dictionary
= NULL
;
1462 if (status
== kCTIStatus_NoError
) {
1463 status
= cti_event_or_response_extract(reply
, &result_dictionary
);
1464 if (status
== kCTIStatus_NoError
) {
1465 xpc_object_t
*value
= xpc_dictionary_get_array(result_dictionary
, "value");
1466 if (value
== NULL
) {
1467 INFO("cti_internal_prefix_reply_callback: prefixes array not present in IPv6:Routes event.");
1469 status
= cti_parse_prefixes_array(&vec
, value
);
1473 if (callback
!= NULL
) {
1474 callback(conn_ref
->context
, vec
, status
);
1476 INFO("Not calling callback.");
1479 RELEASE_HERE(vec
, cti_prefix_vec_finalize
);
1484 cti_get_prefix_list(cti_connection_t
*ref
, void *NULLABLE context
, cti_prefix_reply_t NONNULL callback
,
1485 dispatch_queue_t NONNULL client_queue
)
1488 xpc_object_t dict
= xpc_dictionary_create(NULL
, NULL
, 0);
1490 cti_callback_t app_callback
;
1491 app_callback
.prefix_reply
= callback
;
1493 xpc_dictionary_set_string(dict
, "interface", "org.wpantund.v1");
1494 xpc_dictionary_set_string(dict
, "path", "/org/wpantund/utun2");
1495 xpc_dictionary_set_string(dict
, "method", "PropGet");
1496 xpc_dictionary_set_string(dict
, "property_name", "IPv6:Routes");
1498 errx
= setup_for_command(ref
, client_queue
, "get_prefix_list", "IPv6:Routes", dict
, "WpanctlCmd",
1499 context
, app_callback
, cti_internal_prefix_reply_callback
);
1506 cti_events_discontinue(cti_connection_t ref
)
1508 cti_connection_release(ref
);
1509 return kCTIStatus_NoError
;
1515 // c-file-style: "bsd"
1516 // c-basic-offset: 4
1518 // indent-tabs-mode: nil