Home | History | Annotate | Line # | Download | only in ServiceRegistration
      1  1.1  christos /* cti-services.c
      2  1.1  christos  *
      3  1.1  christos  * Copyright (c) 2020-2024 Apple Inc. All rights reserved.
      4  1.1  christos  *
      5  1.1  christos  * Licensed under the Apache License, Version 2.0 (the "License");
      6  1.1  christos  * you may not use this file except in compliance with the License.
      7  1.1  christos  * You may obtain a copy of the License at
      8  1.1  christos  *
      9  1.1  christos  *     https://www.apache.org/licenses/LICENSE-2.0
     10  1.1  christos  *
     11  1.1  christos  * Unless required by applicable law or agreed to in writing, software
     12  1.1  christos  * distributed under the License is distributed on an "AS IS" BASIS,
     13  1.1  christos  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  1.1  christos  * See the License for the specific language governing permissions and
     15  1.1  christos  * limitations under the License.
     16  1.1  christos  *
     17  1.1  christos  * This code adds border router support to 3rd party HomeKit Routers as part of Apples commitment to the CHIP project.
     18  1.1  christos  *
     19  1.1  christos  * Concise Thread Interface for Thread Border router control.
     20  1.1  christos  */
     21  1.1  christos 
     22  1.1  christos 
     23  1.1  christos #include <netinet/in.h>
     24  1.1  christos #include <string.h>
     25  1.1  christos #include <stdlib.h>
     26  1.1  christos 
     27  1.1  christos #include <Block.h>
     28  1.1  christos #include <os/log.h>
     29  1.1  christos #include <netinet/in.h>
     30  1.1  christos #include <net/if.h>
     31  1.1  christos #include <arpa/inet.h>
     32  1.1  christos #include <netinet6/in6_var.h>
     33  1.1  christos #include <netinet/icmp6.h>
     34  1.1  christos #include <netinet6/nd6.h>
     35  1.1  christos #include "xpc_clients.h"
     36  1.1  christos #include "cti-services.h"
     37  1.1  christos typedef xpc_object_t object_t;
     38  1.1  christos typedef void (*cti_internal_callback_t)(cti_connection_t NONNULL conn_ref, object_t reply, cti_status_t status);
     39  1.1  christos 
     40  1.1  christos 
     41  1.1  christos //*************************************************************************************************************
     42  1.1  christos // Globals
     43  1.1  christos 
     44  1.1  christos #include "cti-common.h"
     45  1.1  christos 
     46  1.1  christos static int client_serial_number;
     47  1.1  christos 
     48  1.1  christos struct _cti_connection_t
     49  1.1  christos {
     50  1.1  christos     int ref_count;
     51  1.1  christos 
     52  1.1  christos     // Callback function ptr for Client
     53  1.1  christos     cti_callback_t callback;
     54  1.1  christos 
     55  1.1  christos     // xpc_connection between client and daemon
     56  1.1  christos     xpc_connection_t NULLABLE connection;
     57  1.1  christos 
     58  1.1  christos     // Before we can send commands, we have to check in, so when starting up, we stash the initial command
     59  1.1  christos     // here until we get an acknowledgment for the checkin.
     60  1.1  christos     object_t *first_command;
     61  1.1  christos 
     62  1.1  christos     // Queue specified by client for scheduling its Callback
     63  1.1  christos     dispatch_queue_t NULLABLE client_queue;
     64  1.1  christos 
     65  1.1  christos     // For commands that fetch properties and also track properties, this will contain the name of the property
     66  1.1  christos     // for which events are requested.
     67  1.1  christos     const char *property_name;
     68  1.1  christos 
     69  1.1  christos     // For commands that fetch string properties, this will contain the name of the string property to look for
     70  1.1  christos     // in the answer
     71  1.1  christos     const char *return_property_name;
     72  1.1  christos 
     73  1.1  christos     cti_internal_callback_t NONNULL internal_callback;
     74  1.1  christos 
     75  1.1  christos     // Client context
     76  1.1  christos     void *NULLABLE context;
     77  1.1  christos 
     78  1.1  christos     // Printed when debugging the event handler
     79  1.1  christos     const char *NONNULL command_name;
     80  1.1  christos 
     81  1.1  christos     // This connection's serial number, based on the global client_serial_number above.
     82  1.1  christos     int serial;
     83  1.1  christos 
     84  1.1  christos     // True if we've gotten a response to the check-in message.
     85  1.1  christos     bool checked_in;
     86  1.1  christos };
     87  1.1  christos 
     88  1.1  christos //*************************************************************************************************************
     89  1.1  christos // Utility Functions
     90  1.1  christos 
     91  1.1  christos static void
     92  1.1  christos cti_connection_finalize(cti_connection_t ref)
     93  1.1  christos {
     94  1.1  christos     if (ref->first_command != NULL) {
     95  1.1  christos         xpc_release(ref->first_command);
     96  1.1  christos         ref->first_command = NULL;
     97  1.1  christos     }
     98  1.1  christos     free(ref);
     99  1.1  christos }
    100  1.1  christos 
    101  1.1  christos #define cti_connection_release(ref) cti_connection_release_(ref, __FILE__, __LINE__)
    102  1.1  christos static void
    103  1.1  christos cti_connection_release_(cti_connection_t ref, const char *file, int line)
    104  1.1  christos {
    105  1.1  christos     ref->callback.reply = NULL;
    106  1.1  christos     RELEASE(ref, cti_connection);
    107  1.1  christos }
    108  1.1  christos 
    109  1.1  christos static void
    110  1.1  christos cti_xpc_connection_finalize(void *context)
    111  1.1  christos {
    112  1.1  christos     cti_connection_t ref = context;
    113  1.1  christos     INFO("[CX%d] " PUB_S_SRP, ref->serial, ref->command_name);
    114  1.1  christos     cti_connection_release(context);
    115  1.1  christos }
    116  1.1  christos 
    117  1.1  christos static char *
    118  1.1  christos cti_xpc_copy_description(object_t object)
    119  1.1  christos {
    120  1.1  christos     xpc_type_t type = xpc_get_type(object);
    121  1.1  christos     if (type == XPC_TYPE_UINT64) {
    122  1.1  christos         uint64_t num = xpc_uint64_get_value(object);
    123  1.1  christos         char buf[23];
    124  1.1  christos         snprintf(buf, sizeof buf, "%llu", num);
    125  1.1  christos         return strdup(buf);
    126  1.1  christos     } else if (type == XPC_TYPE_INT64) {
    127  1.1  christos         int64_t num = xpc_int64_get_value(object);
    128  1.1  christos         char buf[23];
    129  1.1  christos         snprintf(buf, sizeof buf, "%lld", num);
    130  1.1  christos         return strdup(buf);
    131  1.1  christos     } else if (type == XPC_TYPE_STRING) {
    132  1.1  christos         const char *str = xpc_string_get_string_ptr(object);
    133  1.1  christos         size_t len = xpc_string_get_length(object);
    134  1.1  christos         char *ret = malloc(len + 3);
    135  1.1  christos         if (ret != NULL) {
    136  1.1  christos             *ret = '"';
    137  1.1  christos             strlcpy(ret + 1, str, len + 1);
    138  1.1  christos             ret[len + 1] = '"';
    139  1.1  christos             ret[len + 2] = 0;
    140  1.1  christos             return ret;
    141  1.1  christos         }
    142  1.1  christos     } else if (type == XPC_TYPE_DATA) {
    143  1.1  christos         const uint8_t *data = xpc_data_get_bytes_ptr(object);
    144  1.1  christos         size_t i, len = xpc_data_get_length(object);
    145  1.1  christos         char *ret = malloc(len * 2 + 3);
    146  1.1  christos         if (ret != NULL) {
    147  1.1  christos             ret[0] = '0';
    148  1.1  christos             ret[1] = 'x';
    149  1.1  christos             for (i = 0; i < len; i++) {
    150  1.1  christos                 snprintf(ret + i * 2, 3, "%02x", data[i]);
    151  1.1  christos             }
    152  1.1  christos             return ret;
    153  1.1  christos         }
    154  1.1  christos     } else if (type == XPC_TYPE_BOOL) {
    155  1.1  christos         bool flag = xpc_bool_get_value(object);
    156  1.1  christos         if (flag) {
    157  1.1  christos             return strdup("true");
    158  1.1  christos         } else {
    159  1.1  christos             return strdup("false");
    160  1.1  christos         }
    161  1.1  christos     } else if (type == XPC_TYPE_ARRAY) {
    162  1.1  christos         size_t avail, vlen, len = 0, i, count = xpc_array_get_count(object);
    163  1.1  christos         char **values = malloc(count * sizeof(*values));
    164  1.1  christos         char *ret, *p_ret;
    165  1.1  christos         if (values == NULL) {
    166  1.1  christos             return NULL;
    167  1.1  christos         }
    168  1.1  christos         xpc_array_apply(object, ^bool (size_t index, object_t value) {
    169  1.1  christos                 values[index] = cti_xpc_copy_description(value);
    170  1.1  christos                 return true;
    171  1.1  christos             });
    172  1.1  christos         for (i = 0; i < count; i++) {
    173  1.1  christos             if (values[i] == NULL) {
    174  1.1  christos                 len += 6;
    175  1.1  christos             } else {
    176  1.1  christos                 len += strlen(values[i]) + 2;
    177  1.1  christos             }
    178  1.1  christos         }
    179  1.1  christos         ret = malloc(len + 3);
    180  1.1  christos         p_ret = ret;
    181  1.1  christos         avail = len + 1;
    182  1.1  christos         *p_ret++ = '[';
    183  1.1  christos         --avail;
    184  1.1  christos         for (i = 0; i < count; i++) {
    185  1.1  christos             if (p_ret != NULL) {
    186  1.1  christos                 snprintf(p_ret, avail, "%s%s%s", i == 0 ? "" : " ", values[i] != NULL ? values[i] : "NULL", (i + 1 == count) ? "" : ",");
    187  1.1  christos                 vlen = strlen(p_ret);
    188  1.1  christos                 p_ret += vlen;
    189  1.1  christos                 avail -= vlen;
    190  1.1  christos             }
    191  1.1  christos             if (values[i] != NULL) {
    192  1.1  christos                 free(values[i]);
    193  1.1  christos             }
    194  1.1  christos         }
    195  1.1  christos         *p_ret++ = ']';
    196  1.1  christos         *p_ret++ = 0;
    197  1.1  christos         free(values);
    198  1.1  christos         return ret;
    199  1.1  christos     }
    200  1.1  christos     return xpc_copy_description(object);
    201  1.1  christos }
    202  1.1  christos 
    203  1.1  christos static void
    204  1.1  christos cti_log_object(const char *context, int serial, const char *command, const char *preamble, const char *divide, object_t *object, char *indent)
    205  1.1  christos {
    206  1.1  christos     xpc_type_t type = xpc_get_type(object);
    207  1.1  christos     static char no_indent[] = "";
    208  1.1  christos     if (indent == NULL) {
    209  1.1  christos         indent = no_indent;
    210  1.1  christos     }
    211  1.1  christos     char *new_indent;
    212  1.1  christos     size_t depth;
    213  1.1  christos     char *desc;
    214  1.1  christos     char *compound_begin;
    215  1.1  christos     char *compound_end;
    216  1.1  christos 
    217  1.1  christos     if (type == XPC_TYPE_DICTIONARY || type == XPC_TYPE_ARRAY) {
    218  1.1  christos         bool compact = true;
    219  1.1  christos         bool *p_compact = &compact;
    220  1.1  christos         if (type == XPC_TYPE_DICTIONARY) {
    221  1.1  christos             compound_begin = "{";
    222  1.1  christos             compound_end = "}";
    223  1.1  christos             xpc_dictionary_apply(object, ^bool (const char *__unused key, object_t value) {
    224  1.1  christos                     xpc_type_t sub_type = xpc_get_type(value);
    225  1.1  christos                     if (sub_type == XPC_TYPE_DICTIONARY) {
    226  1.1  christos                         *p_compact = false;
    227  1.1  christos                     } else if (sub_type == XPC_TYPE_ARRAY) {
    228  1.1  christos                         xpc_array_apply(value, ^bool (size_t __unused index, object_t sub_value) {
    229  1.1  christos                                 xpc_type_t sub_sub_type = xpc_get_type(sub_value);
    230  1.1  christos                                 if (sub_sub_type == XPC_TYPE_DICTIONARY || sub_sub_type == XPC_TYPE_ARRAY) {
    231  1.1  christos                                     *p_compact = false;
    232  1.1  christos                                 }
    233  1.1  christos                                 return true;
    234  1.1  christos                             });
    235  1.1  christos                     }
    236  1.1  christos                     return true;
    237  1.1  christos                 });
    238  1.1  christos         } else {
    239  1.1  christos             compound_begin = "[";
    240  1.1  christos             compound_end = "]";
    241  1.1  christos             xpc_array_apply(object, ^bool (size_t __unused index, object_t value) {
    242  1.1  christos                     xpc_type_t sub_type = xpc_get_type(value);
    243  1.1  christos                     if (sub_type == XPC_TYPE_DICTIONARY || sub_type == XPC_TYPE_ARRAY) {
    244  1.1  christos                         *p_compact = false;
    245  1.1  christos                     }
    246  1.1  christos                     return true;
    247  1.1  christos                 });
    248  1.1  christos         }
    249  1.1  christos         if (compact) {
    250  1.1  christos             size_t i, count;
    251  1.1  christos             const char **keys = NULL;
    252  1.1  christos             char **values;
    253  1.1  christos             char linebuf[160], *p_space;
    254  1.1  christos             size_t space_avail = sizeof(linebuf);
    255  1.1  christos             bool first = true;
    256  1.1  christos 
    257  1.1  christos             if (type == XPC_TYPE_DICTIONARY) {
    258  1.1  christos                 count = xpc_dictionary_get_count(object);
    259  1.1  christos             } else {
    260  1.1  christos                 count = xpc_array_get_count(object);
    261  1.1  christos             }
    262  1.1  christos 
    263  1.1  christos             values = malloc(count * sizeof(*values));
    264  1.1  christos             if (values == NULL) {
    265  1.1  christos                 INFO("[CX%d] no memory", serial);
    266  1.1  christos                 return;
    267  1.1  christos             }
    268  1.1  christos             if (type == XPC_TYPE_DICTIONARY) {
    269  1.1  christos                 int index = 0, *p_index = &index;
    270  1.1  christos                 keys = malloc(count * sizeof(*keys));
    271  1.1  christos                 if (keys == NULL) {
    272  1.1  christos                     free(values);
    273  1.1  christos                     INFO("[CX%d] no memory", serial);
    274  1.1  christos                 }
    275  1.1  christos                 xpc_dictionary_apply(object, ^bool (const char *key, object_t value) {
    276  1.1  christos                         values[*p_index] = cti_xpc_copy_description(value);
    277  1.1  christos                         keys[*p_index] = key;
    278  1.1  christos                         (*p_index)++;
    279  1.1  christos                         return true;
    280  1.1  christos                     });
    281  1.1  christos             } else {
    282  1.1  christos                 xpc_array_apply(object, ^bool (size_t index, object_t value) {
    283  1.1  christos                         values[index] = cti_xpc_copy_description(value);
    284  1.1  christos                         return true;
    285  1.1  christos                     });
    286  1.1  christos             }
    287  1.1  christos             p_space = linebuf;
    288  1.1  christos             for (i = 0; i < count; i++) {
    289  1.1  christos                 char *str = values[i];
    290  1.1  christos                 size_t len;
    291  1.1  christos                 char *eol = "";
    292  1.1  christos                 bool emitted = false;
    293  1.1  christos                 if (str == NULL) {
    294  1.1  christos                     str = "NULL";
    295  1.1  christos                     len = 6;
    296  1.1  christos                 } else {
    297  1.1  christos                     len = strlen(str) + 2;
    298  1.1  christos                 }
    299  1.1  christos                 if (type == XPC_TYPE_DICTIONARY) {
    300  1.1  christos #ifdef __clang_analyzer__
    301  1.1  christos                     len = 2;
    302  1.1  christos #else
    303  1.1  christos                     len += strlen(keys[i]) + 2; // "key: "
    304  1.1  christos #endif
    305  1.1  christos                 }
    306  1.1  christos                 if (len + 1 > space_avail) {
    307  1.1  christos                     if (i + 1 == count) {
    308  1.1  christos                         eol = compound_end;
    309  1.1  christos                     }
    310  1.1  christos                     if (space_avail != sizeof(linebuf)) {
    311  1.1  christos                         if (first) {
    312  1.1  christos                             INFO("[CX%d] " PUB_S_SRP "(" PUB_S_SRP "): " PUB_S_SRP PUB_S_SRP PUB_S_SRP " " PUB_S_SRP
    313  1.1  christos                                  PUB_S_SRP PUB_S_SRP, serial, context, command,
    314  1.1  christos                                  indent, preamble, divide, compound_begin, linebuf, eol);
    315  1.1  christos                             first = false;
    316  1.1  christos                         } else {
    317  1.1  christos                             INFO("[CX%d] " PUB_S_SRP "(" PUB_S_SRP "): " PUB_S_SRP PUB_S_SRP PUB_S_SRP " +" PUB_S_SRP
    318  1.1  christos                                  PUB_S_SRP, serial, context, command,
    319  1.1  christos                                  indent, preamble, divide, linebuf, eol);
    320  1.1  christos                         }
    321  1.1  christos                         space_avail = sizeof linebuf;
    322  1.1  christos                         p_space = linebuf;
    323  1.1  christos                     }
    324  1.1  christos                     if (len + 1 > space_avail) {
    325  1.1  christos                         if (type == XPC_TYPE_DICTIONARY) {
    326  1.1  christos #ifndef __clang_analyzer__
    327  1.1  christos                             if (first) {
    328  1.1  christos                                 INFO("[CX%d] " PUB_S_SRP "(" PUB_S_SRP "): " PUB_S_SRP PUB_S_SRP PUB_S_SRP " " PUB_S_SRP
    329  1.1  christos                                      PUB_S_SRP ": " PUB_S_SRP PUB_S_SRP, serial, context, command,
    330  1.1  christos                                      indent, preamble, divide, compound_begin, keys[i], str, eol);
    331  1.1  christos                                 first = false;
    332  1.1  christos                             } else {
    333  1.1  christos                                 INFO("[CX%d] " PUB_S_SRP "(" PUB_S_SRP "): " PUB_S_SRP PUB_S_SRP PUB_S_SRP " +" PUB_S_SRP
    334  1.1  christos                                      ": " PUB_S_SRP PUB_S_SRP, serial, context, command,
    335  1.1  christos                                      indent, preamble, divide, keys[i], str, eol);
    336  1.1  christos                             }
    337  1.1  christos #endif
    338  1.1  christos                         } else {
    339  1.1  christos                             if (first) {
    340  1.1  christos                                 INFO("[CX%d] " PUB_S_SRP "(" PUB_S_SRP "): " PUB_S_SRP PUB_S_SRP PUB_S_SRP " " PUB_S_SRP
    341  1.1  christos                                      PUB_S_SRP PUB_S_SRP, serial, context, command,
    342  1.1  christos                                      indent, preamble, divide, compound_begin, str, eol);
    343  1.1  christos                                 first = false;
    344  1.1  christos                             } else {
    345  1.1  christos                                 INFO("[CX%d] " PUB_S_SRP "(" PUB_S_SRP "): " PUB_S_SRP PUB_S_SRP PUB_S_SRP " +" PUB_S_SRP
    346  1.1  christos                                      PUB_S_SRP, serial, context, command, indent, preamble, divide, str, eol);
    347  1.1  christos                             }
    348  1.1  christos                         }
    349  1.1  christos                         emitted = true;
    350  1.1  christos                     }
    351  1.1  christos                 }
    352  1.1  christos                 if (!emitted) {
    353  1.1  christos                     if (type == XPC_TYPE_DICTIONARY) {
    354  1.1  christos #ifndef __clang_analyzer__
    355  1.1  christos                         snprintf(p_space, space_avail, "%s%s: %s%s", i == 0 ? "" : " ", keys[i], str, i + 1 == count ? "" : ",");
    356  1.1  christos #endif
    357  1.1  christos                     } else {
    358  1.1  christos                         snprintf(p_space, space_avail, "%s%s%s", i == 0 ? "" : " ", str, i + 1 == count ? "" : ",");
    359  1.1  christos                     }
    360  1.1  christos                     len = strlen(p_space);
    361  1.1  christos                     p_space += len;
    362  1.1  christos                     space_avail -= len;
    363  1.1  christos                 }
    364  1.1  christos                 if (values[i] != NULL) {
    365  1.1  christos                     free(values[i]);
    366  1.1  christos                     values[i] = NULL;
    367  1.1  christos                 }
    368  1.1  christos             }
    369  1.1  christos             if (linebuf != p_space) {
    370  1.1  christos                 if (first) {
    371  1.1  christos                     INFO("[CX%d] " PUB_S_SRP "(" PUB_S_SRP "): " PUB_S_SRP PUB_S_SRP PUB_S_SRP " " PUB_S_SRP PUB_S_SRP
    372  1.1  christos                          PUB_S_SRP, serial, context, command,
    373  1.1  christos                          indent, preamble, divide, compound_begin, linebuf, compound_end);
    374  1.1  christos                 } else {
    375  1.1  christos                     INFO("[CX%d] " PUB_S_SRP "(" PUB_S_SRP "): " PUB_S_SRP PUB_S_SRP PUB_S_SRP " + " PUB_S_SRP PUB_S_SRP,
    376  1.1  christos                          serial, context, command, indent, preamble, divide, linebuf, compound_end);
    377  1.1  christos                 }
    378  1.1  christos             }
    379  1.1  christos             free(values);
    380  1.1  christos             if (keys != NULL) {
    381  1.1  christos                 free(keys);
    382  1.1  christos             }
    383  1.1  christos         } else {
    384  1.1  christos             depth = strlen(indent);
    385  1.1  christos             new_indent = malloc(depth + 3);
    386  1.1  christos             if (new_indent == NULL) {
    387  1.1  christos                 new_indent = indent;
    388  1.1  christos             } else {
    389  1.1  christos                 memset(new_indent, ' ', depth + 2);
    390  1.1  christos                 new_indent[depth + 2] = 0;
    391  1.1  christos             }
    392  1.1  christos             if (type == XPC_TYPE_DICTIONARY) {
    393  1.1  christos                 xpc_dictionary_apply(object, ^bool (const char *key, object_t value) {
    394  1.1  christos                     cti_log_object(context, serial, command, key, ": ", value, new_indent);
    395  1.1  christos                     return true;
    396  1.1  christos                 });
    397  1.1  christos             } else {
    398  1.1  christos                 xpc_array_apply(object, ^bool (size_t index, object_t value) {
    399  1.1  christos                     char numbuf[23];
    400  1.1  christos                     snprintf(numbuf, sizeof(numbuf), "%zd", index);
    401  1.1  christos                     cti_log_object(context, serial, command, numbuf, ": ", value, new_indent);
    402  1.1  christos                     return true;
    403  1.1  christos                 });
    404  1.1  christos             }
    405  1.1  christos             if (new_indent != indent) {
    406  1.1  christos                 free(new_indent);
    407  1.1  christos             }
    408  1.1  christos         }
    409  1.1  christos     } else {
    410  1.1  christos         desc = cti_xpc_copy_description(object);
    411  1.1  christos         INFO("[CX%d] " PUB_S_SRP "(" PUB_S_SRP "): " PUB_S_SRP PUB_S_SRP PUB_S_SRP " " PUB_S_SRP,
    412  1.1  christos              serial, context, command, indent, preamble, divide, desc);
    413  1.1  christos         free(desc);
    414  1.1  christos     }
    415  1.1  christos }
    416  1.1  christos 
    417  1.1  christos static void
    418  1.1  christos cti_event_handler(object_t event, cti_connection_t conn_ref)
    419  1.1  christos {
    420  1.1  christos     if (event == XPC_ERROR_CONNECTION_INVALID) {
    421  1.1  christos         INFO("[CX%d] (" PUB_S_SRP "): cleanup", conn_ref->serial, conn_ref->command_name);
    422  1.1  christos         if (conn_ref->callback.reply != NULL) {
    423  1.1  christos             conn_ref->internal_callback(conn_ref, event, kCTIStatus_Disconnected);
    424  1.1  christos         } else {
    425  1.1  christos             INFO("[CX%d] No callback", conn_ref->serial);
    426  1.1  christos         }
    427  1.1  christos         if (conn_ref->connection != NULL) {
    428  1.1  christos             INFO("[CX%d] releasing connection %p", conn_ref->serial, conn_ref->connection);
    429  1.1  christos             xpc_release(conn_ref->connection);
    430  1.1  christos             conn_ref->connection = NULL;
    431  1.1  christos         }
    432  1.1  christos         return;
    433  1.1  christos     }
    434  1.1  christos 
    435  1.1  christos     if (conn_ref->connection == NULL) {
    436  1.1  christos         cti_log_object("cti_event_handler NULL connection",
    437  1.1  christos                        conn_ref->serial, conn_ref->command_name, "", "", event, "");
    438  1.1  christos         return;
    439  1.1  christos     }
    440  1.1  christos 
    441  1.1  christos     if (xpc_get_type(event) == XPC_TYPE_DICTIONARY) {
    442  1.1  christos         cti_log_object("cti_event_handler", conn_ref->serial, conn_ref->command_name, "", "", event, "");
    443  1.1  christos         if (!conn_ref->checked_in) {
    444  1.1  christos             object_t command_result = xpc_dictionary_get_value(event, "commandResult");
    445  1.1  christos             int status = 0;
    446  1.1  christos             if (command_result != NULL) {
    447  1.1  christos                 status = (int)xpc_int64_get_value(command_result);
    448  1.1  christos                 if (status == 0) {
    449  1.1  christos                     object_t command_data = xpc_dictionary_get_value(event, "commandData");
    450  1.1  christos                     if (command_data == NULL) {
    451  1.1  christos                         status = 0;
    452  1.1  christos                     } else {
    453  1.1  christos                         object_t ret_value = xpc_dictionary_get_value(command_data, "ret");
    454  1.1  christos                         if (ret_value == NULL) {
    455  1.1  christos                             status = 0;
    456  1.1  christos                         } else {
    457  1.1  christos                             status = (int)xpc_int64_get_value(ret_value);
    458  1.1  christos                         }
    459  1.1  christos                     }
    460  1.1  christos                 }
    461  1.1  christos             }
    462  1.1  christos 
    463  1.1  christos             if (status != 0) {
    464  1.1  christos                 conn_ref->internal_callback(conn_ref, event, kCTIStatus_UnknownError);
    465  1.1  christos                 INFO("[CX%d] canceling xpc connection %p", conn_ref->serial, conn_ref->connection);
    466  1.1  christos                 xpc_connection_cancel(conn_ref->connection);
    467  1.1  christos             } else if (conn_ref->property_name != NULL) {
    468  1.1  christos                 // We're meant to both get the property and subscribe to events on it.
    469  1.1  christos                 object_t *dict = xpc_dictionary_create(NULL, NULL, 0);
    470  1.1  christos                 if (dict == NULL) {
    471  1.1  christos                     ERROR("[CX%d] cti_event_handler(" PUB_S_SRP "): no memory, canceling %p.",
    472  1.1  christos                           conn_ref->serial, conn_ref->command_name, conn_ref->connection);
    473  1.1  christos                     xpc_connection_cancel(conn_ref->connection);
    474  1.1  christos                 } else {
    475  1.1  christos                     object_t *array = xpc_array_create(NULL, 0);
    476  1.1  christos                     if (array == NULL) {
    477  1.1  christos                         ERROR("[CX%d] cti_event_handler(" PUB_S_SRP "): no memory, canceling %p.",
    478  1.1  christos                               conn_ref->serial, conn_ref->command_name, conn_ref->connection);
    479  1.1  christos                         xpc_connection_cancel(conn_ref->connection);
    480  1.1  christos                     } else {
    481  1.1  christos                         xpc_dictionary_set_string(dict, "command", "eventsOn");
    482  1.1  christos                         xpc_dictionary_set_string(dict, "clientName", "srp-mdns-proxy");
    483  1.1  christos                         xpc_dictionary_set_value(dict, "eventList", array);
    484  1.1  christos                         xpc_array_set_string(array, XPC_ARRAY_APPEND, conn_ref->property_name);
    485  1.1  christos                         conn_ref->property_name = NULL;
    486  1.1  christos                         cti_log_object("cti_event_handler/events on",
    487  1.1  christos                                        conn_ref->serial, conn_ref->command_name, "", "", dict, "");
    488  1.1  christos                         INFO("[CX%d] sending message on connection %p", conn_ref->serial, conn_ref->connection);
    489  1.1  christos                         xpc_connection_send_message_with_reply(conn_ref->connection, dict, conn_ref->client_queue,
    490  1.1  christos                                                                ^(object_t in_event) {
    491  1.1  christos                                                                    cti_event_handler(in_event, conn_ref);
    492  1.1  christos                                                                });
    493  1.1  christos                         xpc_release(array);
    494  1.1  christos                     }
    495  1.1  christos                     xpc_release(dict);
    496  1.1  christos                 }
    497  1.1  christos             } else {
    498  1.1  christos                 object_t *message = conn_ref->first_command;
    499  1.1  christos                 conn_ref->first_command = NULL;
    500  1.1  christos                 cti_log_object("cti_event_handler/command is",
    501  1.1  christos                                conn_ref->serial, conn_ref->command_name, "", "", message, "");
    502  1.1  christos                 conn_ref->checked_in = true;
    503  1.1  christos 
    504  1.1  christos                 INFO("[CX%d] sending message on connection %p", conn_ref->serial, conn_ref->connection);
    505  1.1  christos                 xpc_connection_send_message_with_reply(conn_ref->connection, message, conn_ref->client_queue,
    506  1.1  christos                                                        ^(object_t in_event) {
    507  1.1  christos                                                            cti_event_handler(in_event, conn_ref);
    508  1.1  christos                                                        });
    509  1.1  christos                 xpc_release(message);
    510  1.1  christos             }
    511  1.1  christos         } else {
    512  1.1  christos             conn_ref->internal_callback(conn_ref, event, kCTIStatus_NoError);
    513  1.1  christos         }
    514  1.1  christos     } else {
    515  1.1  christos         cti_log_object("cti_event_handler/other", conn_ref->serial, conn_ref->command_name, "", "", event, "");
    516  1.1  christos         ERROR("[CX%d] cti_event_handler: Unexpected Connection Error [" PUB_S_SRP "]",
    517  1.1  christos               conn_ref->serial, xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION));
    518  1.1  christos         conn_ref->internal_callback(conn_ref, NULL, kCTIStatus_DaemonNotRunning);
    519  1.1  christos         if (event != XPC_ERROR_CONNECTION_INTERRUPTED) {
    520  1.1  christos             INFO("[CX%d] canceling xpc connection %p", conn_ref->serial, conn_ref->connection);
    521  1.1  christos             xpc_connection_cancel(conn_ref->connection);
    522  1.1  christos         }
    523  1.1  christos     }
    524  1.1  christos }
    525  1.1  christos 
    526  1.1  christos // Creates a new cti_ Connection Reference(cti_connection_t)
    527  1.1  christos static cti_status_t
    528  1.1  christos init_connection(cti_connection_t *ref, const char *servname, object_t *dict, const char *command_name,
    529  1.1  christos                 const char *property_name, const char *return_property_name, void *context, cti_callback_t app_callback,
    530  1.1  christos                 cti_internal_callback_t internal_callback, run_context_t client_queue,
    531  1.1  christos                 const char *file, int line)
    532  1.1  christos {
    533  1.1  christos     // Use an cti_connection_t on the stack to be captured in the blocks below, rather than
    534  1.1  christos     // capturing the cti_connection_t* owned by the client
    535  1.1  christos #ifdef MALLOC_DEBUG_LOGGING
    536  1.1  christos     cti_connection_t conn_ref = debug_calloc(1, sizeof(struct _cti_connection_t), file, line);
    537  1.1  christos #else
    538  1.1  christos     cti_connection_t conn_ref = calloc(1, sizeof(struct _cti_connection_t));
    539  1.1  christos #endif
    540  1.1  christos     if (conn_ref == NULL) {
    541  1.1  christos         ERROR("no memory to allocate!");
    542  1.1  christos         return kCTIStatus_NoMemory;
    543  1.1  christos     }
    544  1.1  christos     conn_ref->serial = client_serial_number;
    545  1.1  christos     client_serial_number++;
    546  1.1  christos 
    547  1.1  christos     // We always retain a reference for the caller, even if the caller doesn't actually hold the reference.
    548  1.1  christos     // Calls that do not result in repeated callbacks release this reference after calling the callback.
    549  1.1  christos     // Such calls do not return a reference to the caller, so there is no chance of a double release.
    550  1.1  christos     // Calls that result in repeated callbacks have to release the reference by calling cti_events_discontinue.
    551  1.1  christos     // If this isn't done, the reference will never be released.
    552  1.1  christos     RETAIN(conn_ref, cti_connection);
    553  1.1  christos 
    554  1.1  christos     if (client_queue == NULL) {
    555  1.1  christos         client_queue = dispatch_get_main_queue();
    556  1.1  christos     }
    557  1.1  christos 
    558  1.1  christos     // Initialize the cti_connection_t
    559  1.1  christos     dispatch_retain(client_queue);
    560  1.1  christos     conn_ref->command_name = command_name;
    561  1.1  christos     conn_ref->property_name = property_name;
    562  1.1  christos     conn_ref->return_property_name = return_property_name;
    563  1.1  christos     conn_ref->context = context;
    564  1.1  christos     conn_ref->client_queue = client_queue;
    565  1.1  christos     conn_ref->callback = app_callback;
    566  1.1  christos     conn_ref->internal_callback = internal_callback;
    567  1.1  christos     conn_ref->connection = xpc_connection_create_mach_service(servname, conn_ref->client_queue,
    568  1.1  christos                                                               XPC_CONNECTION_MACH_SERVICE_PRIVILEGED);
    569  1.1  christos     INFO("[CX%d] xpc connection: %p", conn_ref->serial, conn_ref->connection);
    570  1.1  christos     conn_ref->first_command = dict;
    571  1.1  christos     xpc_retain(dict);
    572  1.1  christos 
    573  1.1  christos     cti_log_object("init_connection/command", conn_ref->serial, conn_ref->command_name, "", "", dict, "");
    574  1.1  christos 
    575  1.1  christos     if (conn_ref->connection == NULL)
    576  1.1  christos     {
    577  1.1  christos         ERROR("conn_ref/lib_q is NULL");
    578  1.1  christos         if (conn_ref != NULL) {
    579  1.1  christos             RELEASE_HERE(conn_ref, cti_connection);
    580  1.1  christos         }
    581  1.1  christos         return kCTIStatus_NoMemory;
    582  1.1  christos     }
    583  1.1  christos 
    584  1.1  christos     RETAIN_HERE(conn_ref, cti_connection); // For the event handler.
    585  1.1  christos     xpc_connection_set_event_handler(conn_ref->connection, ^(object_t event) { cti_event_handler(event, conn_ref); });
    586  1.1  christos     xpc_connection_set_finalizer_f(conn_ref->connection, cti_xpc_connection_finalize);
    587  1.1  christos     xpc_connection_set_context(conn_ref->connection, conn_ref);
    588  1.1  christos     xpc_connection_resume(conn_ref->connection);
    589  1.1  christos 
    590  1.1  christos     char srp_name[] = "srp-mdns-proxy";
    591  1.1  christos     char client_name[sizeof(srp_name) + 20];
    592  1.1  christos     snprintf(client_name, sizeof client_name, "%s-%d", srp_name, conn_ref->serial);
    593  1.1  christos 
    594  1.1  christos     object_t checkin_command = xpc_dictionary_create(NULL, NULL, 0);
    595  1.1  christos 
    596  1.1  christos     xpc_dictionary_set_string(checkin_command, "command", "checkIn");
    597  1.1  christos     xpc_dictionary_set_string(checkin_command, "clientName", client_name);
    598  1.1  christos 
    599  1.1  christos     cti_log_object("init_connection/checkin", conn_ref->serial, conn_ref->command_name, "", "", checkin_command, "");
    600  1.1  christos     INFO("[CX%d] sending message on connection %p", conn_ref->serial, conn_ref->connection);
    601  1.1  christos     xpc_connection_send_message_with_reply(conn_ref->connection, checkin_command, conn_ref->client_queue,
    602  1.1  christos                                            ^(object_t event) { cti_event_handler(event, conn_ref); });
    603  1.1  christos 
    604  1.1  christos     xpc_release(checkin_command);
    605  1.1  christos     if (ref) {
    606  1.1  christos         *ref = conn_ref;
    607  1.1  christos     }
    608  1.1  christos     return kCTIStatus_NoError;
    609  1.1  christos }
    610  1.1  christos 
    611  1.1  christos static cti_status_t
    612  1.1  christos setup_for_command(cti_connection_t *ref, run_context_t client_queue, const char *command_name,
    613  1.1  christos                   const char *property_name, const char *return_property_name, object_t dict, const char *command,
    614  1.1  christos                   void *context, cti_callback_t app_callback, cti_internal_callback_t internal_callback,
    615  1.1  christos                   bool events_only, const char *file, int line)
    616  1.1  christos {
    617  1.1  christos     cti_status_t errx = kCTIStatus_NoError;
    618  1.1  christos 
    619  1.1  christos     // Sanity Checks
    620  1.1  christos     if (app_callback.reply == NULL || internal_callback == NULL)
    621  1.1  christos     {
    622  1.1  christos         ERROR(PUB_S_SRP ": NULL cti_connection_t OR Callback OR Client_Queue parameter", command_name);
    623  1.1  christos         return kCTIStatus_BadParam;
    624  1.1  christos     }
    625  1.1  christos 
    626  1.1  christos     // Get conn_ref from init_connection()
    627  1.1  christos     if (events_only) {
    628  1.1  christos         xpc_dictionary_set_string(dict, "command", "eventsOn");
    629  1.1  christos         object_t *array = xpc_array_create(NULL, 0);
    630  1.1  christos         if (array != NULL) {
    631  1.1  christos             xpc_array_set_string(array, XPC_ARRAY_APPEND, property_name);
    632  1.1  christos             xpc_dictionary_set_value(dict, "eventList", array);
    633  1.1  christos             property_name = NULL;
    634  1.1  christos             xpc_release(array);
    635  1.1  christos         } else {
    636  1.1  christos             return kCTIStatus_NoMemory;
    637  1.1  christos         }
    638  1.1  christos     } else {
    639  1.1  christos         xpc_dictionary_set_string(dict, "command", command);
    640  1.1  christos     }
    641  1.1  christos 
    642  1.1  christos     errx = init_connection(ref, "com.apple.wpantund.xpc", dict, command_name, property_name, return_property_name,
    643  1.1  christos                            context, app_callback, internal_callback, client_queue, file, line);
    644  1.1  christos     if (errx) // On error init_connection() leaves *conn_ref set to NULL
    645  1.1  christos     {
    646  1.1  christos         ERROR(PUB_S_SRP ": Since init_connection() returned %d error returning w/o sending msg", command_name, errx);
    647  1.1  christos         return errx;
    648  1.1  christos     }
    649  1.1  christos 
    650  1.1  christos     return errx;
    651  1.1  christos }
    652  1.1  christos 
    653  1.1  christos static void
    654  1.1  christos cti_internal_event_reply_callback(cti_connection_t NONNULL conn_ref, object_t __unused reply, cti_status_t status)
    655  1.1  christos {
    656  1.1  christos     cti_reply_t callback;
    657  1.1  christos     INFO("[CX%d] conn_ref = %p", conn_ref != NULL ? conn_ref->serial : 0, conn_ref);
    658  1.1  christos     callback = conn_ref->callback.reply;
    659  1.1  christos     if (callback != NULL) {
    660  1.1  christos         callback(conn_ref->context, status);
    661  1.1  christos     }
    662  1.1  christos }
    663  1.1  christos 
    664  1.1  christos static void
    665  1.1  christos cti_internal_reply_callback(cti_connection_t NONNULL conn_ref, object_t reply, cti_status_t status)
    666  1.1  christos {
    667  1.1  christos     cti_internal_event_reply_callback(conn_ref, reply, status);
    668  1.1  christos     conn_ref->callback.reply = NULL;
    669  1.1  christos     if (conn_ref->connection != NULL) {
    670  1.1  christos         INFO("[CX%d] canceling connection %p", conn_ref->serial, conn_ref->connection);
    671  1.1  christos         xpc_connection_cancel(conn_ref->connection);
    672  1.1  christos     }
    673  1.1  christos     cti_connection_release(conn_ref);
    674  1.1  christos }
    675  1.1  christos 
    676  1.1  christos cti_status_t
    677  1.1  christos cti_add_service_(srp_server_t *UNUSED server, void *context, cti_reply_t callback, run_context_t client_queue,
    678  1.1  christos                  uint32_t enterprise_number, const uint8_t *NONNULL service_data, size_t service_data_length,
    679  1.1  christos                  const uint8_t *server_data, size_t server_data_length, const char *file, int line)
    680  1.1  christos {
    681  1.1  christos     cti_callback_t app_callback;
    682  1.1  christos     app_callback.reply = callback;
    683  1.1  christos     cti_status_t errx;
    684  1.1  christos     object_t dict = xpc_dictionary_create(NULL, NULL, 0);
    685  1.1  christos 
    686  1.1  christos     xpc_dictionary_set_data(dict, "service_data", service_data, service_data_length);
    687  1.1  christos     if (server_data != NULL) {
    688  1.1  christos         xpc_dictionary_set_data(dict, "server_data", server_data, server_data_length);
    689  1.1  christos     }
    690  1.1  christos     xpc_dictionary_set_uint64(dict, "enterprise_number", enterprise_number);
    691  1.1  christos     xpc_dictionary_set_string(dict, "interface", "org.wpantund.v1");
    692  1.1  christos     xpc_dictionary_set_string(dict, "path", "/org/wpantund/utun2");
    693  1.1  christos     xpc_dictionary_set_string(dict, "method", "ServiceAdd");
    694  1.1  christos     xpc_dictionary_set_bool(dict, "stable", true);
    695  1.1  christos 
    696  1.1  christos     errx = setup_for_command(NULL, client_queue, "add_service", NULL, NULL, dict, "WpanctlCmd",
    697  1.1  christos                              context, app_callback, cti_internal_reply_callback, false, file, line);
    698  1.1  christos     xpc_release(dict);
    699  1.1  christos 
    700  1.1  christos     return errx;
    701  1.1  christos }
    702  1.1  christos 
    703  1.1  christos cti_status_t
    704  1.1  christos cti_remove_service_(srp_server_t *UNUSED server, void *context, cti_reply_t callback, run_context_t client_queue,
    705  1.1  christos                     uint32_t enterprise_number, const uint8_t *NONNULL service_data, size_t service_data_length,
    706  1.1  christos                     const char *file, int line)
    707  1.1  christos {
    708  1.1  christos     cti_callback_t app_callback;
    709  1.1  christos     app_callback.reply = callback;
    710  1.1  christos     cti_status_t errx;
    711  1.1  christos     object_t dict = xpc_dictionary_create(NULL, NULL, 0);
    712  1.1  christos 
    713  1.1  christos     xpc_dictionary_set_data(dict, "service_data", service_data, service_data_length);
    714  1.1  christos     xpc_dictionary_set_uint64(dict, "enterprise_number", enterprise_number);
    715  1.1  christos     xpc_dictionary_set_string(dict, "interface", "org.wpantund.v1");
    716  1.1  christos     xpc_dictionary_set_string(dict, "path", "/org/wpantund/utun2");
    717  1.1  christos     xpc_dictionary_set_string(dict, "method", "ServiceRemove");
    718  1.1  christos 
    719  1.1  christos     errx = setup_for_command(NULL, client_queue, "remove_service", NULL, NULL, dict, "WpanctlCmd",
    720  1.1  christos                              context, app_callback, cti_internal_reply_callback, false, file, line);
    721  1.1  christos     xpc_release(dict);
    722  1.1  christos 
    723  1.1  christos     return errx;
    724  1.1  christos }
    725  1.1  christos 
    726  1.1  christos static cti_status_t
    727  1.1  christos cti_do_prefix(void *context, cti_reply_t callback, run_context_t client_queue,
    728  1.1  christos               struct in6_addr *prefix, int prefix_length, bool on_mesh, bool preferred, bool slaac, bool stable, bool adding,
    729  1.1  christos               int priority, const char *file, int line)
    730  1.1  christos {
    731  1.1  christos     cti_callback_t app_callback;
    732  1.1  christos     app_callback.reply = callback;
    733  1.1  christos     cti_status_t errx;
    734  1.1  christos     object_t dict = xpc_dictionary_create(NULL, NULL, 0);
    735  1.1  christos 
    736  1.1  christos     if (dict == NULL) {
    737  1.1  christos         ERROR("cti_do_prefix: no memory for command dictionary.");
    738  1.1  christos         return kCTIStatus_NoMemory;
    739  1.1  christos     }
    740  1.1  christos     xpc_dictionary_set_bool(dict, "preferred", preferred);
    741  1.1  christos     if (adding) {
    742  1.1  christos         xpc_dictionary_set_uint64(dict, "preferredLifetime", ND6_INFINITE_LIFETIME);
    743  1.1  christos         xpc_dictionary_set_uint64(dict, "validLifetime", ND6_INFINITE_LIFETIME);
    744  1.1  christos     } else {
    745  1.1  christos         xpc_dictionary_set_uint64(dict, "preferredLifetime", 0);
    746  1.1  christos         xpc_dictionary_set_uint64(dict, "validLifetime", 0);
    747  1.1  christos     }
    748  1.1  christos     xpc_dictionary_set_int64(dict, "prefix_length", 16);
    749  1.1  christos     xpc_dictionary_set_bool(dict, "dhcp", false);
    750  1.1  christos     xpc_dictionary_set_data(dict, "prefix", prefix, sizeof(*prefix));
    751  1.1  christos     xpc_dictionary_set_string(dict, "interface", "org.wpantund.v1");
    752  1.1  christos     xpc_dictionary_set_uint64(dict, "prefix_len_in_bits", prefix_length);
    753  1.1  christos     xpc_dictionary_set_bool(dict, "slaac", slaac);
    754  1.1  christos     xpc_dictionary_set_bool(dict, "onMesh", on_mesh);
    755  1.1  christos     xpc_dictionary_set_bool(dict, "configure", false);
    756  1.1  christos     xpc_dictionary_set_string(dict, "path", "/org/wpantund/utun2");
    757  1.1  christos     xpc_dictionary_set_string(dict, "method", "ConfigGateway");
    758  1.1  christos     xpc_dictionary_set_bool(dict, "stable", stable);
    759  1.1  christos     xpc_dictionary_set_bool(dict, "defaultRoute", true);
    760  1.1  christos     xpc_dictionary_set_int64(dict, "priority", priority);
    761  1.1  christos 
    762  1.1  christos     errx = setup_for_command(NULL, client_queue, adding ? "add_prefix" : "remove_prefix", NULL, NULL, dict, "WpanctlCmd",
    763  1.1  christos                              context, app_callback, cti_internal_reply_callback, false, file, line);
    764  1.1  christos     xpc_release(dict);
    765  1.1  christos 
    766  1.1  christos     return errx;
    767  1.1  christos }
    768  1.1  christos 
    769  1.1  christos cti_status_t
    770  1.1  christos cti_add_prefix_(srp_server_t *UNUSED server, void *context, cti_reply_t callback, run_context_t client_queue,
    771  1.1  christos                 struct in6_addr *prefix, int prefix_length, bool on_mesh, bool preferred, bool slaac, bool stable,
    772  1.1  christos                 int priority, const char *file, int line)
    773  1.1  christos {
    774  1.1  christos     return cti_do_prefix(context, callback, client_queue, prefix, prefix_length, on_mesh, preferred, slaac, stable,
    775  1.1  christos                          true, priority, file, line);
    776  1.1  christos }
    777  1.1  christos 
    778  1.1  christos cti_status_t
    779  1.1  christos cti_remove_prefix_(srp_server_t *UNUSED server, void *NULLABLE context, cti_reply_t NONNULL callback,
    780  1.1  christos                    run_context_t NULLABLE client_queue, struct in6_addr *NONNULL prefix, int prefix_length,
    781  1.1  christos                    const char *file, int line)
    782  1.1  christos {
    783  1.1  christos     return cti_do_prefix(context, callback, client_queue, prefix, prefix_length, false, false, false, false, false, 0,
    784  1.1  christos                          file, line);
    785  1.1  christos }
    786  1.1  christos 
    787  1.1  christos cti_status_t
    788  1.1  christos cti_add_route_(srp_server_t *UNUSED server, void *context, cti_reply_t callback, run_context_t client_queue,
    789  1.1  christos                struct in6_addr *prefix, int prefix_length, int priority, int domain_id,
    790  1.1  christos                bool stable, bool nat64, const char *file, int line)
    791  1.1  christos {
    792  1.1  christos     cti_callback_t app_callback;
    793  1.1  christos     app_callback.reply = callback;
    794  1.1  christos     cti_status_t errx;
    795  1.1  christos     object_t dict = xpc_dictionary_create(NULL, NULL, 0);
    796  1.1  christos 
    797  1.1  christos     if (dict == NULL) {
    798  1.1  christos         ERROR("cti_do_prefix: no memory for command dictionary.");
    799  1.1  christos         return kCTIStatus_NoMemory;
    800  1.1  christos     }
    801  1.1  christos     xpc_dictionary_set_string(dict, "method", "RouteAdd");
    802  1.1  christos     xpc_dictionary_set_string(dict, "interface", "org.wpantund.v1");
    803  1.1  christos     xpc_dictionary_set_string(dict, "path", "/org/wpantund/utun2");
    804  1.1  christos     xpc_dictionary_set_uint64(dict, "prefix_bits_len", prefix_length);
    805  1.1  christos     xpc_dictionary_set_data(dict, "prefix_bits", prefix, sizeof(*prefix));
    806  1.1  christos     xpc_dictionary_set_uint64(dict, "domain_id", domain_id);
    807  1.1  christos     xpc_dictionary_set_int64(dict, "priority", priority);
    808  1.1  christos     xpc_dictionary_set_bool(dict, "stable", stable);
    809  1.1  christos     xpc_dictionary_set_bool(dict, "nat64", nat64);
    810  1.1  christos 
    811  1.1  christos     errx = setup_for_command(NULL, client_queue, "add_route", NULL, NULL, dict, "WpanctlCmd",
    812  1.1  christos                              context, app_callback, cti_internal_reply_callback, false, file, line);
    813  1.1  christos     xpc_release(dict);
    814  1.1  christos 
    815  1.1  christos     return errx;
    816  1.1  christos }
    817  1.1  christos 
    818  1.1  christos cti_status_t
    819  1.1  christos cti_remove_route_(srp_server_t *UNUSED server, void *NULLABLE context, cti_reply_t NONNULL callback,
    820  1.1  christos                   run_context_t NULLABLE client_queue, struct in6_addr *NONNULL prefix, int prefix_length,
    821  1.1  christos                   int domain_id, const char *file, int line)
    822  1.1  christos {
    823  1.1  christos     cti_callback_t app_callback;
    824  1.1  christos     app_callback.reply = callback;
    825  1.1  christos     cti_status_t errx;
    826  1.1  christos     object_t dict = xpc_dictionary_create(NULL, NULL, 0);
    827  1.1  christos 
    828  1.1  christos     if (dict == NULL) {
    829  1.1  christos         ERROR("cti_do_prefix: no memory for command dictionary.");
    830  1.1  christos         return kCTIStatus_NoMemory;
    831  1.1  christos     }
    832  1.1  christos     xpc_dictionary_set_string(dict, "method", "RouteRemove");
    833  1.1  christos     xpc_dictionary_set_string(dict, "interface", "org.wpantund.v1");
    834  1.1  christos     xpc_dictionary_set_string(dict, "path", "/org/wpantund/utun2");
    835  1.1  christos     xpc_dictionary_set_uint64(dict, "prefix_len_in_bits", prefix_length);
    836  1.1  christos     xpc_dictionary_set_data(dict, "prefix_bytes", prefix, sizeof(*prefix));
    837  1.1  christos     xpc_dictionary_set_uint64(dict, "domain_id", domain_id);
    838  1.1  christos 
    839  1.1  christos     errx = setup_for_command(NULL, client_queue, "remove_route", NULL, NULL, dict, "WpanctlCmd",
    840  1.1  christos                              context, app_callback, cti_internal_reply_callback, false, file, line);
    841  1.1  christos     xpc_release(dict);
    842  1.1  christos 
    843  1.1  christos     return errx;
    844  1.1  christos }
    845  1.1  christos 
    846  1.1  christos static void
    847  1.1  christos cti_internal_string_event_reply(cti_connection_t NONNULL conn_ref, object_t reply, cti_status_t status_in)
    848  1.1  christos {
    849  1.1  christos     cti_string_property_reply_t callback = conn_ref->callback.string_property_reply;
    850  1.1  christos     xpc_retain(reply);
    851  1.1  christos     cti_status_t status = status_in;
    852  1.1  christos     const char *string_value = NULL;
    853  1.1  christos     if (status == kCTIStatus_NoError) {
    854  1.1  christos         // Initial reply (events do a get as well as subscribing to events, and the responses look different.
    855  1.1  christos         xpc_object_t command_result_value = xpc_dictionary_get_value(reply, "commandResult");
    856  1.1  christos         if (command_result_value != NULL) {
    857  1.1  christos             uint64_t command_result = xpc_int64_get_value(command_result_value);
    858  1.1  christos             if (command_result != 0) {
    859  1.1  christos                 ERROR("[CX%d] nonzero result %llu", conn_ref->serial, command_result);
    860  1.1  christos                 status = kCTIStatus_UnknownError;
    861  1.1  christos             } else {
    862  1.1  christos                 object_t result_dictionary = xpc_dictionary_get_dictionary(reply, "commandData");
    863  1.1  christos                 if (status == kCTIStatus_NoError) {
    864  1.1  christos                     if (result_dictionary != NULL) {
    865  1.1  christos                         const char *property_name = xpc_dictionary_get_string(result_dictionary, "property_name");
    866  1.1  christos                         if (property_name == NULL || strcmp(property_name, conn_ref->return_property_name)) {
    867  1.1  christos                             status = kCTIStatus_UnknownError;
    868  1.1  christos                         } else {
    869  1.1  christos                             string_value = xpc_dictionary_get_string(result_dictionary, "value");
    870  1.1  christos                             if (string_value == NULL) {
    871  1.1  christos                                 status = kCTIStatus_UnknownError;
    872  1.1  christos                             }
    873  1.1  christos                         }
    874  1.1  christos                     } else {
    875  1.1  christos                         status = kCTIStatus_UnknownError;
    876  1.1  christos                     }
    877  1.1  christos                 }
    878  1.1  christos             }
    879  1.1  christos         } else {
    880  1.1  christos             xpc_object_t event_data_dict = xpc_dictionary_get_dictionary(reply, "eventData");
    881  1.1  christos             if (event_data_dict == NULL) {
    882  1.1  christos                 ERROR("[CX%d] no eventData dictionary", conn_ref->serial);
    883  1.1  christos                 status = kCTIStatus_UnknownError;
    884  1.1  christos             } else {
    885  1.1  christos                 // Event data looks like { "v_type" : 13, "path": <string>, "value": <string>, "key": <property_name> }
    886  1.1  christos                 xpc_object_t value_array = xpc_dictionary_get_array(event_data_dict, "value");
    887  1.1  christos                 if (value_array == NULL) {
    888  1.1  christos                     ERROR("[CX%d] eventData dictionary contains no 'value' key", conn_ref->serial);
    889  1.1  christos                     status = kCTIStatus_UnknownError;
    890  1.1  christos                 } else {
    891  1.1  christos                     size_t count = xpc_array_get_count(value_array);
    892  1.1  christos                     if (count < 1) {
    893  1.1  christos                         ERROR("[CX%d] eventData value array has no elements", conn_ref->serial);
    894  1.1  christos                         status = kCTIStatus_UnknownError;
    895  1.1  christos                     } else {
    896  1.1  christos                         if (count > 1) {
    897  1.1  christos                             ERROR("[CX%d] eventData value array has %zd elements", conn_ref->serial, count);
    898  1.1  christos                             // This is weird, but we're not going to deliberately fail because of it.
    899  1.1  christos                         }
    900  1.1  christos                         string_value = xpc_array_get_string(value_array, 0);
    901  1.1  christos                         if (string_value == NULL) {
    902  1.1  christos                             ERROR("[CX%d] eventData value array's first element is not a string", conn_ref->serial);
    903  1.1  christos                             status = kCTIStatus_UnknownError;
    904  1.1  christos                         }
    905  1.1  christos                     }
    906  1.1  christos                 }
    907  1.1  christos             }
    908  1.1  christos         }
    909  1.1  christos     }
    910  1.1  christos     if (callback != NULL) {
    911  1.1  christos         callback(conn_ref->context, string_value, status);
    912  1.1  christos     }
    913  1.1  christos     xpc_release(reply);
    914  1.1  christos }
    915  1.1  christos 
    916  1.1  christos static void
    917  1.1  christos cti_internal_string_property_reply(cti_connection_t NONNULL conn_ref, object_t reply, cti_status_t status_in)
    918  1.1  christos {
    919  1.1  christos     cti_internal_string_event_reply(conn_ref, reply, status_in);
    920  1.1  christos     conn_ref->callback.reply = NULL;
    921  1.1  christos     if (conn_ref->connection != NULL) {
    922  1.1  christos         INFO("[CX%d] canceling connection %p", conn_ref->serial, conn_ref->connection);
    923  1.1  christos         xpc_connection_cancel(conn_ref->connection);
    924  1.1  christos     }
    925  1.1  christos     RELEASE_HERE(conn_ref, cti_connection);
    926  1.1  christos }
    927  1.1  christos 
    928  1.1  christos cti_status_t
    929  1.1  christos cti_get_tunnel_name_(srp_server_t *UNUSED server, void *NULLABLE context, cti_string_property_reply_t NONNULL callback,
    930  1.1  christos                      run_context_t NULLABLE client_queue, const char *file, int line)
    931  1.1  christos {
    932  1.1  christos     cti_callback_t app_callback;
    933  1.1  christos     app_callback.string_property_reply = callback;
    934  1.1  christos     cti_status_t errx;
    935  1.1  christos     object_t dict = xpc_dictionary_create(NULL, NULL, 0);
    936  1.1  christos 
    937  1.1  christos     xpc_dictionary_set_string(dict, "interface", "org.wpantund.v1");
    938  1.1  christos     xpc_dictionary_set_string(dict, "path", "/org/wpantund/utun2");
    939  1.1  christos     xpc_dictionary_set_string(dict, "method", "PropGet");
    940  1.1  christos     xpc_dictionary_set_string(dict, "property_name", "Config:TUN:InterfaceName");
    941  1.1  christos 
    942  1.1  christos     errx = setup_for_command(NULL, client_queue, "get_tunnel_name", NULL, "Config:TUN:InterfaceName", dict, "WpanctlCmd",
    943  1.1  christos                              context, app_callback, cti_internal_string_property_reply, false, file, line);
    944  1.1  christos     xpc_release(dict);
    945  1.1  christos 
    946  1.1  christos     return errx;
    947  1.1  christos }
    948  1.1  christos 
    949  1.1  christos cti_status_t
    950  1.1  christos cti_track_active_data_set_(srp_server_t *UNUSED server, cti_connection_t *ref, void *NULLABLE context,
    951  1.1  christos                            cti_reply_t NONNULL callback,
    952  1.1  christos                            run_context_t NULLABLE client_queue, const char *file, int line)
    953  1.1  christos {
    954  1.1  christos     cti_callback_t app_callback;
    955  1.1  christos     app_callback.reply = callback;
    956  1.1  christos     cti_status_t errx;
    957  1.1  christos     object_t dict = xpc_dictionary_create(NULL, NULL, 0);
    958  1.1  christos 
    959  1.1  christos     xpc_dictionary_set_string(dict, "interface", "org.wpantund.v1");
    960  1.1  christos     xpc_dictionary_set_string(dict, "path", "/org/wpantund/utun2");
    961  1.1  christos 
    962  1.1  christos     errx = setup_for_command(ref, client_queue, "track_active_data_set", "ActiveDataSetChanged", NULL, dict, "WpanctlCmd",
    963  1.1  christos                              context, app_callback, cti_internal_event_reply_callback, true, file, line);
    964  1.1  christos     xpc_release(dict);
    965  1.1  christos 
    966  1.1  christos     return errx;
    967  1.1  christos }
    968  1.1  christos 
    969  1.1  christos cti_status_t
    970  1.1  christos cti_get_mesh_local_prefix_(srp_server_t *UNUSED server, void *NULLABLE context,
    971  1.1  christos                            cti_string_property_reply_t NONNULL callback,
    972  1.1  christos                            run_context_t NULLABLE client_queue, const char *file, int line)
    973  1.1  christos {
    974  1.1  christos     cti_callback_t app_callback;
    975  1.1  christos     app_callback.string_property_reply = callback;
    976  1.1  christos     cti_status_t errx;
    977  1.1  christos     object_t dict = xpc_dictionary_create(NULL, NULL, 0);
    978  1.1  christos 
    979  1.1  christos     xpc_dictionary_set_string(dict, "interface", "org.wpantund.v1");
    980  1.1  christos     xpc_dictionary_set_string(dict, "path", "/org/wpantund/utun2");
    981  1.1  christos     xpc_dictionary_set_string(dict, "method", "PropGet");
    982  1.1  christos     xpc_dictionary_set_string(dict, "property_name", "IPv6:MeshLocalPrefix");
    983  1.1  christos 
    984  1.1  christos     errx = setup_for_command(NULL, client_queue, "get_mesh_local_prefix", NULL,
    985  1.1  christos                              "IPv6:MeshLocalPrefix", dict, "WpanctlCmd",
    986  1.1  christos                              context, app_callback, cti_internal_string_property_reply, false, file, line);
    987  1.1  christos     xpc_release(dict);
    988  1.1  christos 
    989  1.1  christos     return errx;
    990  1.1  christos }
    991  1.1  christos 
    992  1.1  christos cti_status_t
    993  1.1  christos cti_get_mesh_local_address_(srp_server_t *UNUSED server, void *NULLABLE context,
    994  1.1  christos                             cti_string_property_reply_t NONNULL callback, run_context_t NULLABLE client_queue,
    995  1.1  christos                             const char *file, int line)
    996  1.1  christos {
    997  1.1  christos     cti_callback_t app_callback;
    998  1.1  christos     app_callback.string_property_reply = callback;
    999  1.1  christos     cti_status_t errx;
   1000  1.1  christos     object_t dict = xpc_dictionary_create(NULL, NULL, 0);
   1001  1.1  christos 
   1002  1.1  christos     xpc_dictionary_set_string(dict, "interface", "org.wpantund.v1");
   1003  1.1  christos     xpc_dictionary_set_string(dict, "path", "/org/wpantund/utun2");
   1004  1.1  christos     xpc_dictionary_set_string(dict, "method", "PropGet");
   1005  1.1  christos     xpc_dictionary_set_string(dict, "property_name", "IPv6:MeshLocalAddress");
   1006  1.1  christos 
   1007  1.1  christos     errx = setup_for_command(NULL, client_queue, "get_mesh_local_address", NULL,
   1008  1.1  christos                              "IPv6:MeshLocalAddress", dict, "WpanctlCmd", context, app_callback,
   1009  1.1  christos                              cti_internal_string_property_reply, false, file, line);
   1010  1.1  christos     xpc_release(dict);
   1011  1.1  christos 
   1012  1.1  christos     return errx;
   1013  1.1  christos }
   1014  1.1  christos 
   1015  1.1  christos static cti_status_t
   1016  1.1  christos cti_event_or_response_extract(object_t *reply, object_t *result_dictionary)
   1017  1.1  christos {
   1018  1.1  christos     object_t *result = xpc_dictionary_get_dictionary(reply, "commandData");
   1019  1.1  christos     if (result == NULL) {
   1020  1.1  christos         result = xpc_dictionary_get_dictionary(reply, "eventData");
   1021  1.1  christos     } else {
   1022  1.1  christos         int command_status = (int)xpc_dictionary_get_int64(reply, "commandResult");
   1023  1.1  christos         if (command_status != 0) {
   1024  1.1  christos             INFO("nonzero status %d", command_status);
   1025  1.1  christos             return kCTIStatus_UnknownError;
   1026  1.1  christos         }
   1027  1.1  christos     }
   1028  1.1  christos     if (result != NULL) {
   1029  1.1  christos         *result_dictionary = result;
   1030  1.1  christos         return kCTIStatus_NoError;
   1031  1.1  christos     }
   1032  1.1  christos     INFO("null result");
   1033  1.1  christos     return kCTIStatus_UnknownError;
   1034  1.1  christos }
   1035  1.1  christos 
   1036  1.1  christos static void
   1037  1.1  christos cti_internal_state_reply_callback(cti_connection_t NONNULL conn_ref, object_t reply, cti_status_t status_in)
   1038  1.1  christos {
   1039  1.1  christos     cti_state_reply_t callback = conn_ref->callback.state_reply;
   1040  1.1  christos     cti_network_state_t state = kCTI_NCPState_Unknown;
   1041  1.1  christos     cti_status_t status = status_in;
   1042  1.1  christos     if (status == kCTIStatus_NoError) {
   1043  1.1  christos         object_t result_dictionary = NULL;
   1044  1.1  christos         status = cti_event_or_response_extract(reply, &result_dictionary);
   1045  1.1  christos         if (status == kCTIStatus_NoError) {
   1046  1.1  christos             const char *state_name = xpc_dictionary_get_string(result_dictionary, "value");
   1047  1.1  christos             if (state_name == NULL) {
   1048  1.1  christos                 status = kCTIStatus_UnknownError;
   1049  1.1  christos             } else if (!strcmp(state_name, "uninitialized")) {
   1050  1.1  christos                 state = kCTI_NCPState_Uninitialized;
   1051  1.1  christos             } else if (!strcmp(state_name, "uninitialized:fault")) {
   1052  1.1  christos                 state = kCTI_NCPState_Fault;
   1053  1.1  christos             } else if (!strcmp(state_name, "uninitialized:upgrading")) {
   1054  1.1  christos                 state = kCTI_NCPState_Upgrading;
   1055  1.1  christos             } else if (!strcmp(state_name, "offline:deep-sleep")) {
   1056  1.1  christos                 state = kCTI_NCPState_DeepSleep;
   1057  1.1  christos             } else if (!strcmp(state_name, "offline")) {
   1058  1.1  christos                 state = kCTI_NCPState_Offline;
   1059  1.1  christos             } else if (!strcmp(state_name, "offline:commissioned")) {
   1060  1.1  christos                 state = kCTI_NCPState_Commissioned;
   1061  1.1  christos             } else if (!strcmp(state_name, "associating")) {
   1062  1.1  christos                 state = kCTI_NCPState_Associating;
   1063  1.1  christos             } else if (!strcmp(state_name, "associating:credentials-needed")) {
   1064  1.1  christos                 state = kCTI_NCPState_CredentialsNeeded;
   1065  1.1  christos             } else if (!strcmp(state_name, "associated")) {
   1066  1.1  christos                 state = kCTI_NCPState_Associated;
   1067  1.1  christos             } else if (!strcmp(state_name, "associated:no-parent")) {
   1068  1.1  christos                 state = kCTI_NCPState_Isolated;
   1069  1.1  christos             } else if (!strcmp(state_name, "associated:netwake-asleep")) {
   1070  1.1  christos                 state = kCTI_NCPState_NetWake_Asleep;
   1071  1.1  christos             } else if (!strcmp(state_name, "associated:netwake-waking")) {
   1072  1.1  christos                 state = kCTI_NCPState_NetWake_Waking;
   1073  1.1  christos             }
   1074  1.1  christos         }
   1075  1.1  christos     }
   1076  1.1  christos     if (callback != NULL) {
   1077  1.1  christos         INFO("[CX%d] calling callback for %p", conn_ref->serial, conn_ref);
   1078  1.1  christos         callback(conn_ref->context, state, status);
   1079  1.1  christos     }
   1080  1.1  christos }
   1081  1.1  christos 
   1082  1.1  christos cti_status_t
   1083  1.1  christos cti_get_state_(srp_server_t *UNUSED server, cti_connection_t *ref, void *NULLABLE context,
   1084  1.1  christos                cti_state_reply_t NONNULL callback, run_context_t NULLABLE client_queue, const char *file, int line)
   1085  1.1  christos {
   1086  1.1  christos     cti_callback_t app_callback;
   1087  1.1  christos     app_callback.state_reply = callback;
   1088  1.1  christos     cti_status_t errx;
   1089  1.1  christos     object_t dict = xpc_dictionary_create(NULL, NULL, 0);
   1090  1.1  christos 
   1091  1.1  christos     xpc_dictionary_set_string(dict, "interface", "org.wpantund.v1");
   1092  1.1  christos     xpc_dictionary_set_string(dict, "path", "/org/wpantund/utun2");
   1093  1.1  christos     xpc_dictionary_set_string(dict, "method", "PropGet");
   1094  1.1  christos     xpc_dictionary_set_string(dict, "property_name", "NCP:State");
   1095  1.1  christos 
   1096  1.1  christos     errx = setup_for_command(ref, client_queue, "get_state", "NCP:State", NULL, dict, "WpanctlCmd",
   1097  1.1  christos                              context, app_callback, cti_internal_state_reply_callback, false, file, line);
   1098  1.1  christos     xpc_release(dict);
   1099  1.1  christos 
   1100  1.1  christos     return errx;
   1101  1.1  christos }
   1102  1.1  christos 
   1103  1.1  christos typedef const char *cti_property_name_t;
   1104  1.1  christos 
   1105  1.1  christos static void
   1106  1.1  christos cti_internal_uint64_property_callback(cti_connection_t NONNULL conn_ref, object_t reply, cti_status_t status_in)
   1107  1.1  christos {
   1108  1.1  christos     cti_uint64_property_reply_t callback = conn_ref->callback.uint64_property_reply;
   1109  1.1  christos     uint64_t uint64_val = 0;
   1110  1.1  christos     cti_status_t status = status_in;
   1111  1.1  christos     if (status == kCTIStatus_NoError) {
   1112  1.1  christos         object_t result_dictionary = NULL;
   1113  1.1  christos         status = cti_event_or_response_extract(reply, &result_dictionary);
   1114  1.1  christos         if (status == kCTIStatus_NoError) {
   1115  1.1  christos             object_t value = xpc_dictionary_get_value(result_dictionary, "value");
   1116  1.1  christos             if (value == NULL) {
   1117  1.1  christos                 ERROR("[CX%d] no property value.", conn_ref->serial);
   1118  1.1  christos             } else {
   1119  1.1  christos                 if (xpc_get_type(value) == XPC_TYPE_UINT64) {
   1120  1.1  christos                     uint64_val = xpc_dictionary_get_uint64(result_dictionary, "value");
   1121  1.1  christos                 } else if (xpc_get_type(value) == XPC_TYPE_ARRAY) {
   1122  1.1  christos                     size_t count = xpc_array_get_count(value);
   1123  1.1  christos                     if (count != sizeof(uint64_val)) {
   1124  1.1  christos                         goto fail;
   1125  1.1  christos                     }
   1126  1.1  christos                     for (size_t i = 0; i < sizeof(uint64_val); i++) {
   1127  1.1  christos                         object_t element = xpc_array_get_value(value, i);
   1128  1.1  christos                         if (xpc_get_type(element) != XPC_TYPE_UINT64) {
   1129  1.1  christos                             goto fail;
   1130  1.1  christos                         }
   1131  1.1  christos                         uint64_t ev = xpc_array_get_uint64(value, i);
   1132  1.1  christos                         if (ev > 255) {
   1133  1.1  christos                             goto fail;
   1134  1.1  christos                         }
   1135  1.1  christos                         uint64_val = (uint64_val << 8) | ev;
   1136  1.1  christos                     }
   1137  1.1  christos                 } else {
   1138  1.1  christos                     char *value_string;
   1139  1.1  christos                 fail:
   1140  1.1  christos                     value_string = xpc_copy_description(value);
   1141  1.1  christos                     ERROR("[CX%d] property value is " PUB_S_SRP " instead of uint64_t or array of uint64_t byte values.",
   1142  1.1  christos                           conn_ref->serial, value_string);
   1143  1.1  christos                     free(value_string);
   1144  1.1  christos                     status = kCTIStatus_Invalid;
   1145  1.1  christos                 }
   1146  1.1  christos             }
   1147  1.1  christos         }
   1148  1.1  christos     }
   1149  1.1  christos     if (callback != NULL) {
   1150  1.1  christos         callback(conn_ref->context, uint64_val, status);
   1151  1.1  christos     }
   1152  1.1  christos }
   1153  1.1  christos 
   1154  1.1  christos static cti_status_t
   1155  1.1  christos cti_get_uint64_property(cti_connection_t *ref, void *NULLABLE context, cti_uint64_property_reply_t NONNULL callback,
   1156  1.1  christos                         run_context_t NULLABLE client_queue, cti_property_name_t property_name, const char *file, int line)
   1157  1.1  christos {
   1158  1.1  christos     cti_callback_t app_callback;
   1159  1.1  christos     app_callback.uint64_property_reply = callback;
   1160  1.1  christos     cti_status_t errx;
   1161  1.1  christos     object_t dict = xpc_dictionary_create(NULL, NULL, 0);
   1162  1.1  christos 
   1163  1.1  christos     xpc_dictionary_set_string(dict, "interface", "org.wpantund.v1");
   1164  1.1  christos     xpc_dictionary_set_string(dict, "path", "/org/wpantund/utun2");
   1165  1.1  christos     xpc_dictionary_set_string(dict, "method", "PropGet");
   1166  1.1  christos     xpc_dictionary_set_string(dict, "property_name", property_name);
   1167  1.1  christos 
   1168  1.1  christos     errx = setup_for_command(ref, client_queue, "get_uint64_property", property_name, NULL, dict, "WpanctlCmd",
   1169  1.1  christos                              context, app_callback, cti_internal_uint64_property_callback, false, file, line);
   1170  1.1  christos     xpc_release(dict);
   1171  1.1  christos 
   1172  1.1  christos     return errx;
   1173  1.1  christos }
   1174  1.1  christos 
   1175  1.1  christos cti_status_t
   1176  1.1  christos cti_get_partition_id_(srp_server_t *UNUSED server, cti_connection_t NULLABLE *NULLABLE ref, void *NULLABLE context,
   1177  1.1  christos                       cti_uint64_property_reply_t NONNULL callback, run_context_t NULLABLE client_queue,
   1178  1.1  christos                       const char *NONNULL file, int line)
   1179  1.1  christos {
   1180  1.1  christos     return cti_get_uint64_property(ref, context, callback, client_queue, kCTIPropertyPartitionID, file, line);
   1181  1.1  christos }
   1182  1.1  christos 
   1183  1.1  christos cti_status_t
   1184  1.1  christos cti_get_extended_pan_id_(srp_server_t *UNUSED server, cti_connection_t NULLABLE *NULLABLE ref, void *NULLABLE context,
   1185  1.1  christos                          cti_uint64_property_reply_t NONNULL callback, run_context_t NULLABLE client_queue,
   1186  1.1  christos                          const char *NONNULL file, int line)
   1187  1.1  christos {
   1188  1.1  christos     return cti_get_uint64_property(ref, context, callback, client_queue, kCTIPropertyExtendedPANID, file, line);
   1189  1.1  christos }
   1190  1.1  christos 
   1191  1.1  christos static void
   1192  1.1  christos cti_internal_network_node_type_callback(cti_connection_t NONNULL conn_ref, object_t reply, cti_status_t status_in)
   1193  1.1  christos {
   1194  1.1  christos     cti_network_node_type_reply_t callback = conn_ref->callback.network_node_type_reply;
   1195  1.1  christos     cti_network_node_type_t network_node_type = kCTI_NetworkNodeType_Unknown;
   1196  1.1  christos     cti_status_t status = status_in;
   1197  1.1  christos     if (status == kCTIStatus_NoError) {
   1198  1.1  christos         object_t result_dictionary = NULL;
   1199  1.1  christos         status = cti_event_or_response_extract(reply, &result_dictionary);
   1200  1.1  christos         if (status == kCTIStatus_NoError) {
   1201  1.1  christos             object_t value = xpc_dictionary_get_value(result_dictionary, "value");
   1202  1.1  christos             if (value == NULL) {
   1203  1.1  christos                 ERROR("[CX%d] No node type returned.", conn_ref->serial);
   1204  1.1  christos             } else if (xpc_get_type(value) != XPC_TYPE_STRING) {
   1205  1.1  christos                 char *value_string = xpc_copy_description(value);
   1206  1.1  christos                 ERROR("[CX%d] node type type is " PUB_S_SRP " instead of string.", conn_ref->serial, value_string);
   1207  1.1  christos                 free(value_string);
   1208  1.1  christos             } else {
   1209  1.1  christos                 const char *node_type_name = xpc_dictionary_get_string(result_dictionary, "value");
   1210  1.1  christos                 if (!strcmp(node_type_name, "unknown")) {
   1211  1.1  christos                     network_node_type = kCTI_NetworkNodeType_Unknown;
   1212  1.1  christos                 } else if (!strcmp(node_type_name, "router")) {
   1213  1.1  christos                         network_node_type = kCTI_NetworkNodeType_Router;
   1214  1.1  christos                 } else if (!strcmp(node_type_name, "end-device")) {
   1215  1.1  christos                     network_node_type = kCTI_NetworkNodeType_EndDevice;
   1216  1.1  christos                 } else if (!strcmp(node_type_name, "sleepy-end-device")) {
   1217  1.1  christos                     network_node_type = kCTI_NetworkNodeType_SleepyEndDevice;
   1218  1.1  christos                 } else if (!strcmp(node_type_name, "synchronized-sleepy-end-device")) {
   1219  1.1  christos                     network_node_type = kCTI_NetworkNodeType_SynchronizedSleepyEndDevice;
   1220  1.1  christos                 } else if (!strcmp(node_type_name, "nl-lurker")) {
   1221  1.1  christos                     network_node_type = kCTI_NetworkNodeType_NestLurker;
   1222  1.1  christos                 } else if (!strcmp(node_type_name, "commissioner")) {
   1223  1.1  christos                     network_node_type = kCTI_NetworkNodeType_Commissioner;
   1224  1.1  christos                 } else if (!strcmp(node_type_name, "leader")) {
   1225  1.1  christos                     network_node_type = kCTI_NetworkNodeType_Leader;
   1226  1.1  christos                 } else if (!strcmp(node_type_name, "sleepy-router")) {
   1227  1.1  christos                     network_node_type = kCTI_NetworkNodeType_SleepyRouter;
   1228  1.1  christos                 }
   1229  1.1  christos             }
   1230  1.1  christos         }
   1231  1.1  christos     }
   1232  1.1  christos     if (callback != NULL) {
   1233  1.1  christos         INFO("[CX%d] calling callback for %p", conn_ref->serial, conn_ref);
   1234  1.1  christos         callback(conn_ref->context, network_node_type, status);
   1235  1.1  christos     }
   1236  1.1  christos }
   1237  1.1  christos 
   1238  1.1  christos cti_status_t
   1239  1.1  christos cti_get_network_node_type_(srp_server_t *UNUSED server, cti_connection_t *ref, void *NULLABLE context,
   1240  1.1  christos                            cti_network_node_type_reply_t NONNULL callback,
   1241  1.1  christos                            run_context_t NULLABLE client_queue, const char *file, int line)
   1242  1.1  christos {
   1243  1.1  christos     cti_callback_t app_callback;
   1244  1.1  christos     app_callback.network_node_type_reply = callback;
   1245  1.1  christos     cti_status_t errx;
   1246  1.1  christos     object_t dict = xpc_dictionary_create(NULL, NULL, 0);
   1247  1.1  christos 
   1248  1.1  christos     xpc_dictionary_set_string(dict, "interface", "org.wpantund.v1");
   1249  1.1  christos     xpc_dictionary_set_string(dict, "path", "/org/wpantund/utun2");
   1250  1.1  christos     xpc_dictionary_set_string(dict, "method", "PropGet");
   1251  1.1  christos     xpc_dictionary_set_string(dict, "property_name", "Network:NodeType");
   1252  1.1  christos 
   1253  1.1  christos     errx = setup_for_command(ref, client_queue, "get_network_node_type", "Network:NodeType", NULL, dict, "WpanctlCmd",
   1254  1.1  christos                              context, app_callback, cti_internal_network_node_type_callback, false, file, line);
   1255  1.1  christos     xpc_release(dict);
   1256  1.1  christos 
   1257  1.1  christos     return errx;
   1258  1.1  christos }
   1259  1.1  christos 
   1260  1.1  christos static void
   1261  1.1  christos cti_service_finalize(cti_service_t *service)
   1262  1.1  christos {
   1263  1.1  christos     if (service->server != NULL) {
   1264  1.1  christos         free(service->server);
   1265  1.1  christos     }
   1266  1.1  christos     if (service->service != NULL) {
   1267  1.1  christos         free(service->service);
   1268  1.1  christos     }
   1269  1.1  christos     free(service);
   1270  1.1  christos }
   1271  1.1  christos 
   1272  1.1  christos static void
   1273  1.1  christos cti_service_vec_finalize(cti_service_vec_t *services)
   1274  1.1  christos {
   1275  1.1  christos     size_t i;
   1276  1.1  christos 
   1277  1.1  christos     if (services->services != NULL) {
   1278  1.1  christos         for (i = 0; i < services->num; i++) {
   1279  1.1  christos             if (services->services[i] != NULL) {
   1280  1.1  christos                 RELEASE_HERE(services->services[i], cti_service);
   1281  1.1  christos             }
   1282  1.1  christos         }
   1283  1.1  christos         free(services->services);
   1284  1.1  christos     }
   1285  1.1  christos     free(services);
   1286  1.1  christos }
   1287  1.1  christos 
   1288  1.1  christos cti_service_vec_t *
   1289  1.1  christos cti_service_vec_create_(size_t num_services, const char *file, int line)
   1290  1.1  christos {
   1291  1.1  christos     cti_service_vec_t *services = calloc(1, sizeof(*services));
   1292  1.1  christos     if (services != NULL) {
   1293  1.1  christos         if (num_services != 0) {
   1294  1.1  christos             services->services = calloc(num_services, sizeof(cti_service_t *));
   1295  1.1  christos             if (services->services == NULL) {
   1296  1.1  christos                 free(services);
   1297  1.1  christos                 return NULL;
   1298  1.1  christos             }
   1299  1.1  christos         }
   1300  1.1  christos         services->num = num_services;
   1301  1.1  christos         RETAIN(services, cti_service_vec);
   1302  1.1  christos     }
   1303  1.1  christos     return services;
   1304  1.1  christos }
   1305  1.1  christos 
   1306  1.1  christos void
   1307  1.1  christos cti_service_vec_release_(cti_service_vec_t *services, const char *file, int line)
   1308  1.1  christos {
   1309  1.1  christos     RELEASE(services, cti_service_vec);
   1310  1.1  christos }
   1311  1.1  christos 
   1312  1.1  christos cti_service_t *
   1313  1.1  christos cti_service_create_(uint64_t enterprise_number, uint16_t rloc16, uint16_t service_type,
   1314  1.1  christos                     uint16_t service_version, uint8_t *service_data, size_t service_length,
   1315  1.1  christos                     uint8_t *server, size_t server_length, uint16_t service_id, int flags, const char *file, int line)
   1316  1.1  christos {
   1317  1.1  christos     cti_service_t *service = calloc(1, sizeof(*service));
   1318  1.1  christos     if (service != NULL) {
   1319  1.1  christos         service->enterprise_number = enterprise_number;
   1320  1.1  christos         service->service_type = service_type;
   1321  1.1  christos         service->service_version = service_version;
   1322  1.1  christos         service->rloc16 = rloc16;
   1323  1.1  christos         service->service = service_data;
   1324  1.1  christos         service->service_length = service_length;
   1325  1.1  christos         service->server = server;
   1326  1.1  christos         service->server_length = server_length;
   1327  1.1  christos         service->service_id = service_id;
   1328  1.1  christos         service->flags = flags;
   1329  1.1  christos         RETAIN(service, cti_service);
   1330  1.1  christos     }
   1331  1.1  christos     return service;
   1332  1.1  christos }
   1333  1.1  christos 
   1334  1.1  christos void
   1335  1.1  christos cti_service_release_(cti_service_t *service, const char *file, int line)
   1336  1.1  christos {
   1337  1.1  christos     RELEASE(service, cti_service);
   1338  1.1  christos }
   1339  1.1  christos 
   1340  1.1  christos static uint8_t *
   1341  1.1  christos cti_array_to_bytes(object_t array, size_t *length_ret, const char *log_name)
   1342  1.1  christos {
   1343  1.1  christos     size_t length = xpc_array_get_count(array);
   1344  1.1  christos     size_t i;
   1345  1.1  christos     uint8_t *ret;
   1346  1.1  christos 
   1347  1.1  christos     ret = malloc(length);
   1348  1.1  christos     if (ret == NULL) {
   1349  1.1  christos         ERROR(PUB_S_SRP ": no memory for return buffer", log_name);
   1350  1.1  christos         return NULL;
   1351  1.1  christos     }
   1352  1.1  christos 
   1353  1.1  christos     for (i = 0; i < length; i++) {
   1354  1.1  christos         uint64_t v = xpc_array_get_uint64(array, i);
   1355  1.1  christos         ret[i] = v;
   1356  1.1  christos     }
   1357  1.1  christos     *length_ret = length;
   1358  1.1  christos     return ret;
   1359  1.1  christos }
   1360  1.1  christos 
   1361  1.1  christos static cti_status_t
   1362  1.1  christos cti_parse_services_array(cti_service_vec_t **services, object_t services_array)
   1363  1.1  christos {
   1364  1.1  christos     size_t services_array_length = xpc_array_get_count(services_array);
   1365  1.1  christos     size_t i, j;
   1366  1.1  christos     cti_service_vec_t *service_vec;
   1367  1.1  christos     cti_service_t *service;
   1368  1.1  christos     cti_status_t status = kCTIStatus_NoError;
   1369  1.1  christos 
   1370  1.1  christos     service_vec = cti_service_vec_create(services_array_length);
   1371  1.1  christos     if (service_vec == NULL) {
   1372  1.1  christos         return kCTIStatus_NoMemory;
   1373  1.1  christos     }
   1374  1.1  christos 
   1375  1.1  christos     // Array of arrays
   1376  1.1  christos     for (i = 0; i < services_array_length; i++) {
   1377  1.1  christos         object_t service_array = xpc_array_get_value(services_array, i);
   1378  1.1  christos         int match_count = 0;
   1379  1.1  christos         bool matched_enterprisenum = false;
   1380  1.1  christos         bool matched_origin = false;
   1381  1.1  christos         bool matched_rloc16 = false;
   1382  1.1  christos         bool matched_serverdata = false;
   1383  1.1  christos         bool matched_servicedata = false;
   1384  1.1  christos         bool matched_stable = false;
   1385  1.1  christos         bool matched_service_id = false;
   1386  1.1  christos         uint64_t enterprise_number = 0;
   1387  1.1  christos         uint16_t rloc16 = 0;
   1388  1.1  christos         uint8_t *server_data = NULL;
   1389  1.1  christos         size_t server_data_length = 0;
   1390  1.1  christos         uint8_t *service_data = NULL;
   1391  1.1  christos         size_t service_data_length = 0;
   1392  1.1  christos         int flags = 0;
   1393  1.1  christos         uint16_t service_id = 0;
   1394  1.1  christos 
   1395  1.1  christos         if (service_array == NULL) {
   1396  1.1  christos             ERROR("Unable to get service array %zd", i);
   1397  1.1  christos         } else {
   1398  1.1  christos             size_t service_array_length = xpc_array_get_count(service_array);
   1399  1.1  christos             for (j = 0; j < service_array_length; j++) {
   1400  1.1  christos                 object_t *array_sub_dict = xpc_array_get_value(service_array, j);
   1401  1.1  christos                 if (array_sub_dict == NULL) {
   1402  1.1  christos                     ERROR("can't get service_array %zd subdictionary %zd", i, j);
   1403  1.1  christos                     goto service_array_element_failed;
   1404  1.1  christos                 } else {
   1405  1.1  christos                     const char *key = xpc_dictionary_get_string(array_sub_dict, "key");
   1406  1.1  christos                     if (key == NULL) {
   1407  1.1  christos                         ERROR("Invalid services array %zd subdictionary %zd: no key", i, j);
   1408  1.1  christos                         goto service_array_element_failed;
   1409  1.1  christos                     } else if (!strcmp(key, "EnterpriseNumber")) {
   1410  1.1  christos                         if (matched_enterprisenum) {
   1411  1.1  christos                             ERROR("services array %zd: Enterprise number appears twice.", i);
   1412  1.1  christos                             goto service_array_element_failed;
   1413  1.1  christos                         }
   1414  1.1  christos                         enterprise_number = xpc_dictionary_get_uint64(array_sub_dict, "value");
   1415  1.1  christos                         matched_enterprisenum = true;
   1416  1.1  christos                     } else if (!strcmp(key, "Origin")) {
   1417  1.1  christos                         if (matched_origin) {
   1418  1.1  christos                             ERROR("Services array %zd: Origin appears twice.", i);
   1419  1.1  christos                             goto service_array_element_failed;
   1420  1.1  christos                         }
   1421  1.1  christos                         const char *origin_string = xpc_dictionary_get_string(array_sub_dict, "value");
   1422  1.1  christos                         if (origin_string == NULL) {
   1423  1.1  christos                             ERROR("Unable to get origin string from services array %zd", i);
   1424  1.1  christos                             goto service_array_element_failed;
   1425  1.1  christos                         } else if (!strcmp(origin_string, "user")) {
   1426  1.1  christos                             // Not NCP
   1427  1.1  christos                         } else if (!strcmp(origin_string, "ncp")) {
   1428  1.1  christos                             flags |= kCTIFlag_NCP;
   1429  1.1  christos                         } else {
   1430  1.1  christos                             ERROR("unknown origin " PUB_S_SRP, origin_string);
   1431  1.1  christos                             goto service_array_element_failed;
   1432  1.1  christos                         }
   1433  1.1  christos                         matched_origin = true;
   1434  1.1  christos                     } else if (!strcmp(key, "ServerData")) {
   1435  1.1  christos                         if (matched_serverdata) {
   1436  1.1  christos                             ERROR("Services array %zd: Server data appears twice.", i);
   1437  1.1  christos                             goto service_array_element_failed;
   1438  1.1  christos                         }
   1439  1.1  christos                         server_data = cti_array_to_bytes(xpc_dictionary_get_array(array_sub_dict, "value"),
   1440  1.1  christos                                                          &server_data_length, "Server data");
   1441  1.1  christos                         if (server_data == NULL) {
   1442  1.1  christos                             goto service_array_element_failed;
   1443  1.1  christos                         }
   1444  1.1  christos                         matched_serverdata = true;
   1445  1.1  christos                     } else if (!strcmp(key, "ServiceData")) {
   1446  1.1  christos                         if (matched_servicedata) {
   1447  1.1  christos                             ERROR("Services array %zd: Service data appears twice.", i);
   1448  1.1  christos                             goto service_array_element_failed;
   1449  1.1  christos                         }
   1450  1.1  christos                         service_data = cti_array_to_bytes(xpc_dictionary_get_array(array_sub_dict, "value"),
   1451  1.1  christos                                                           &service_data_length, "Service data");
   1452  1.1  christos                         if (service_data == NULL) {
   1453  1.1  christos                             goto service_array_element_failed;
   1454  1.1  christos                         }
   1455  1.1  christos                         matched_servicedata = true;
   1456  1.1  christos                     } else if (!strcmp(key, "Stable")) {
   1457  1.1  christos                         if (matched_stable) {
   1458  1.1  christos                             ERROR("Services array %zd: Stable state appears twice.", i);
   1459  1.1  christos                             goto service_array_element_failed;
   1460  1.1  christos                         }
   1461  1.1  christos                         if (xpc_dictionary_get_bool(array_sub_dict, "value")) {
   1462  1.1  christos                             flags |= kCTIFlag_Stable;
   1463  1.1  christos                         }
   1464  1.1  christos                         matched_stable = true;
   1465  1.1  christos                     } else if (!strcmp(key, "RLOC16")) {
   1466  1.1  christos                         if (matched_rloc16) {
   1467  1.1  christos                             ERROR("Services array %zd: Stable state appears twice.", i);
   1468  1.1  christos                             goto service_array_element_failed;
   1469  1.1  christos                         }
   1470  1.1  christos                         rloc16 = (uint16_t)xpc_dictionary_get_uint64(array_sub_dict, "value");
   1471  1.1  christos                         matched_rloc16 = true;
   1472  1.1  christos                     } else if (!strcmp(key, "ServiceId")) {
   1473  1.1  christos                         if (matched_service_id) {
   1474  1.1  christos                             ERROR("Services array %zd: Stable state appears twice.", i);
   1475  1.1  christos                             goto service_array_element_failed;
   1476  1.1  christos                         }
   1477  1.1  christos                         service_id = (uint16_t)xpc_dictionary_get_int64(array_sub_dict, "value");
   1478  1.1  christos                         matched_service_id = true;
   1479  1.1  christos                     } else {
   1480  1.1  christos                         INFO("Unknown key in service array %zd subdictionary %zd: " PUB_S_SRP, i, j, key);
   1481  1.1  christos                         // Not a failure, but don't count it.
   1482  1.1  christos                         continue;
   1483  1.1  christos                     }
   1484  1.1  christos                     match_count++;
   1485  1.1  christos                 }
   1486  1.1  christos             }
   1487  1.1  christos             if (match_count != 7) {
   1488  1.1  christos                 ERROR("expecting %d sub-dictionaries to service array %zd, but got %d.",
   1489  1.1  christos                       7, i, match_count);
   1490  1.1  christos                 // No service ID is not fatal (yet), but if some other data is missing, that /is/ fatal.
   1491  1.1  christos                 if (match_count < 6 || (match_count == 6 && matched_service_id)) {
   1492  1.1  christos                     goto service_array_element_failed;
   1493  1.1  christos                 }
   1494  1.1  christos             }
   1495  1.1  christos             uint16_t service_type, service_version;
   1496  1.1  christos             if (enterprise_number == THREAD_ENTERPRISE_NUMBER) {
   1497  1.1  christos                 // two-byte service data for anycast service while on-byte for unicast
   1498  1.1  christos                 // and pref-id
   1499  1.1  christos                 if (service_data_length != 1 && service_data_length != 2) {
   1500  1.1  christos                     INFO("Invalid service data: length = %zd", service_data_length);
   1501  1.1  christos                     goto service_array_element_failed;
   1502  1.1  christos                 }
   1503  1.1  christos                 service_type = service_data[0];
   1504  1.1  christos                 service_version = 1;
   1505  1.1  christos             } else {
   1506  1.1  christos                 // We don't support any other enterprise numbers.
   1507  1.1  christos                 service_type = service_version = 0;
   1508  1.1  christos             }
   1509  1.1  christos 
   1510  1.1  christos             service = cti_service_create(enterprise_number, rloc16, service_type, service_version,
   1511  1.1  christos                                          service_data, service_data_length, server_data,
   1512  1.1  christos                                          server_data_length, service_id, flags);
   1513  1.1  christos             if (service == NULL) {
   1514  1.1  christos                 ERROR("Unable to store service %lld %d %d: out of memory.", enterprise_number,
   1515  1.1  christos                       service_type, service_version);
   1516  1.1  christos             } else {
   1517  1.1  christos                 service_data = NULL;
   1518  1.1  christos                 server_data = NULL;
   1519  1.1  christos                 service_vec->services[i] = service;
   1520  1.1  christos             }
   1521  1.1  christos             goto done_with_service_array;
   1522  1.1  christos         service_array_element_failed:
   1523  1.1  christos             if (status == kCTIStatus_NoError) {
   1524  1.1  christos                 status = kCTIStatus_UnknownError;
   1525  1.1  christos             }
   1526  1.1  christos         done_with_service_array:
   1527  1.1  christos             if (server_data != NULL) {
   1528  1.1  christos                 free(server_data);
   1529  1.1  christos             }
   1530  1.1  christos             if (service_data != NULL) {
   1531  1.1  christos                 free(service_data);
   1532  1.1  christos             }
   1533  1.1  christos         }
   1534  1.1  christos     }
   1535  1.1  christos     if (status == kCTIStatus_NoError) {
   1536  1.1  christos         *services = service_vec;
   1537  1.1  christos     } else {
   1538  1.1  christos         if (service_vec != NULL) {
   1539  1.1  christos             RELEASE_HERE(service_vec, cti_service_vec);
   1540  1.1  christos         }
   1541  1.1  christos     }
   1542  1.1  christos     return status;
   1543  1.1  christos }
   1544  1.1  christos 
   1545  1.1  christos static void
   1546  1.1  christos cti_internal_service_reply_callback(cti_connection_t NONNULL conn_ref, object_t reply, cti_status_t status_in)
   1547  1.1  christos {
   1548  1.1  christos     cti_service_reply_t callback = conn_ref->callback.service_reply;
   1549  1.1  christos     cti_service_vec_t *vec = NULL;
   1550  1.1  christos     cti_status_t status = status_in;
   1551  1.1  christos     if (status == kCTIStatus_NoError) {
   1552  1.1  christos         object_t result_dictionary = NULL;
   1553  1.1  christos         status = cti_event_or_response_extract(reply, &result_dictionary);
   1554  1.1  christos         if (status == kCTIStatus_NoError) {
   1555  1.1  christos             object_t *value = xpc_dictionary_get_array(result_dictionary, "value");
   1556  1.1  christos             if (value == NULL) {
   1557  1.1  christos                 INFO("[CX%d] services array not present in Thread:Services event.", conn_ref->serial);
   1558  1.1  christos             } else {
   1559  1.1  christos                 status = cti_parse_services_array(&vec, value);
   1560  1.1  christos             }
   1561  1.1  christos         }
   1562  1.1  christos     }
   1563  1.1  christos     if (callback != NULL) {
   1564  1.1  christos         INFO("[CX%d] calling callback for %p", conn_ref->serial, conn_ref);
   1565  1.1  christos         callback(conn_ref->context, vec, status);
   1566  1.1  christos     }
   1567  1.1  christos     if (vec != NULL) {
   1568  1.1  christos         RELEASE_HERE(vec, cti_service_vec);
   1569  1.1  christos     }
   1570  1.1  christos }
   1571  1.1  christos 
   1572  1.1  christos cti_status_t
   1573  1.1  christos cti_get_service_list_(srp_server_t *UNUSED server, cti_connection_t *ref, void *NULLABLE context,
   1574  1.1  christos                       cti_service_reply_t NONNULL callback, run_context_t NULLABLE client_queue,
   1575  1.1  christos                       const char *file, int line)
   1576  1.1  christos {
   1577  1.1  christos     cti_callback_t app_callback;
   1578  1.1  christos     app_callback.service_reply = callback;
   1579  1.1  christos     cti_status_t errx;
   1580  1.1  christos     object_t dict = xpc_dictionary_create(NULL, NULL, 0);
   1581  1.1  christos 
   1582  1.1  christos     xpc_dictionary_set_string(dict, "interface", "org.wpantund.v1");
   1583  1.1  christos     xpc_dictionary_set_string(dict, "path", "/org/wpantund/utun2");
   1584  1.1  christos     xpc_dictionary_set_string(dict, "method", "PropGet");
   1585  1.1  christos     xpc_dictionary_set_string(dict, "property_name", "Thread:Services");
   1586  1.1  christos 
   1587  1.1  christos     errx = setup_for_command(ref, client_queue, "get_service_list", "Thread:Services", NULL, dict, "WpanctlCmd",
   1588  1.1  christos                              context, app_callback, cti_internal_service_reply_callback, false, file, line);
   1589  1.1  christos     xpc_release(dict);
   1590  1.1  christos 
   1591  1.1  christos     return errx;
   1592  1.1  christos }
   1593  1.1  christos 
   1594  1.1  christos static void
   1595  1.1  christos cti_prefix_finalize(cti_prefix_t *prefix)
   1596  1.1  christos {
   1597  1.1  christos     free(prefix);
   1598  1.1  christos }
   1599  1.1  christos 
   1600  1.1  christos static void
   1601  1.1  christos cti_prefix_vec_finalize(cti_prefix_vec_t *prefixes)
   1602  1.1  christos {
   1603  1.1  christos     size_t i;
   1604  1.1  christos 
   1605  1.1  christos     if (prefixes->prefixes != NULL) {
   1606  1.1  christos         for (i = 0; i < prefixes->num; i++) {
   1607  1.1  christos             if (prefixes->prefixes[i] != NULL) {
   1608  1.1  christos                 RELEASE_HERE(prefixes->prefixes[i], cti_prefix);
   1609  1.1  christos             }
   1610  1.1  christos         }
   1611  1.1  christos         free(prefixes->prefixes);
   1612  1.1  christos     }
   1613  1.1  christos     free(prefixes);
   1614  1.1  christos }
   1615  1.1  christos 
   1616  1.1  christos cti_prefix_vec_t *
   1617  1.1  christos cti_prefix_vec_create_(size_t num_prefixes, const char *file, int line)
   1618  1.1  christos {
   1619  1.1  christos     cti_prefix_vec_t *prefixes = calloc(1, sizeof(*prefixes));
   1620  1.1  christos     if (prefixes != NULL) {
   1621  1.1  christos         if (num_prefixes != 0) {
   1622  1.1  christos             prefixes->prefixes = calloc(num_prefixes, sizeof(cti_prefix_t *));
   1623  1.1  christos             if (prefixes->prefixes == NULL) {
   1624  1.1  christos                 free(prefixes);
   1625  1.1  christos                 return NULL;
   1626  1.1  christos             }
   1627  1.1  christos         }
   1628  1.1  christos         prefixes->num = num_prefixes;
   1629  1.1  christos         RETAIN(prefixes, cti_prefix_vec);
   1630  1.1  christos     }
   1631  1.1  christos     return prefixes;
   1632  1.1  christos }
   1633  1.1  christos 
   1634  1.1  christos void
   1635  1.1  christos cti_prefix_vec_release_(cti_prefix_vec_t *prefixes, const char *file, int line)
   1636  1.1  christos {
   1637  1.1  christos     RELEASE(prefixes, cti_prefix_vec);
   1638  1.1  christos }
   1639  1.1  christos 
   1640  1.1  christos cti_prefix_t *
   1641  1.1  christos cti_prefix_create_(struct in6_addr *prefix, int prefix_length, int metric, int flags, int rloc, bool stable, bool ncp,
   1642  1.1  christos                    const char *file, int line)
   1643  1.1  christos {
   1644  1.1  christos     cti_prefix_t *prefix_ret = calloc(1, sizeof(*prefix_ret));
   1645  1.1  christos     if (prefix != NULL) {
   1646  1.1  christos         prefix_ret->prefix = *prefix;
   1647  1.1  christos         prefix_ret->prefix_length = prefix_length;
   1648  1.1  christos         prefix_ret->metric = metric;
   1649  1.1  christos         prefix_ret->flags = flags;
   1650  1.1  christos         prefix_ret->rloc = rloc;
   1651  1.1  christos         prefix_ret->stable = stable;
   1652  1.1  christos         prefix_ret->ncp = ncp;
   1653  1.1  christos         RETAIN(prefix_ret, cti_prefix);
   1654  1.1  christos     }
   1655  1.1  christos     return prefix_ret;
   1656  1.1  christos }
   1657  1.1  christos 
   1658  1.1  christos void
   1659  1.1  christos cti_prefix_release_(cti_prefix_t *prefix, const char *file, int line)
   1660  1.1  christos {
   1661  1.1  christos     RELEASE(prefix, cti_prefix);
   1662  1.1  christos }
   1663  1.1  christos 
   1664  1.1  christos static cti_status_t
   1665  1.1  christos cti_parse_prefixes_array(cti_prefix_vec_t **vec_ret, object_t prefixes_array)
   1666  1.1  christos {
   1667  1.1  christos     size_t prefixes_array_length = xpc_array_get_count(prefixes_array);
   1668  1.1  christos     size_t i, j;
   1669  1.1  christos     cti_prefix_vec_t *prefixes = cti_prefix_vec_create(prefixes_array_length);
   1670  1.1  christos     cti_status_t status = kCTIStatus_NoError;
   1671  1.1  christos 
   1672  1.1  christos     if (prefixes == NULL) {
   1673  1.1  christos         INFO("no memory.");
   1674  1.1  christos         status = kCTIStatus_NoMemory;
   1675  1.1  christos     } else {
   1676  1.1  christos         // Array of arrays
   1677  1.1  christos         for (i = 0; i < prefixes_array_length; i++) {
   1678  1.1  christos             object_t prefix_array = xpc_array_get_value(prefixes_array, i);
   1679  1.1  christos             int match_count = 0;
   1680  1.1  christos             bool matched_address = false;
   1681  1.1  christos             bool matched_metric = false;
   1682  1.1  christos             const char *destination = NULL;
   1683  1.1  christos             int metric = 0;
   1684  1.1  christos             struct in6_addr prefix_addr;
   1685  1.1  christos 
   1686  1.1  christos             if (prefix_array == NULL) {
   1687  1.1  christos                 ERROR("Unable to get prefix array %zu", i);
   1688  1.1  christos             } else {
   1689  1.1  christos                 size_t prefix_array_length = xpc_array_get_count(prefix_array);
   1690  1.1  christos                 for (j = 0; j < prefix_array_length; j++) {
   1691  1.1  christos                     object_t *array_sub_dict = xpc_array_get_value(prefix_array, j);
   1692  1.1  christos                     if (array_sub_dict == NULL) {
   1693  1.1  christos                         ERROR("can't get prefix_array %zu subdictionary %zu", i, j);
   1694  1.1  christos                         goto done_with_prefix_array;
   1695  1.1  christos                     } else {
   1696  1.1  christos                         const char *key = xpc_dictionary_get_string(array_sub_dict, "key");
   1697  1.1  christos                         if (key == NULL) {
   1698  1.1  christos                             ERROR("Invalid prefixes array %zu subdictionary %zu: no key", i, j);
   1699  1.1  christos                             goto done_with_prefix_array;
   1700  1.1  christos                         }
   1701  1.1  christos                         // Fix me: when <rdar://problem/59371674> is fixed, remove Addreess key test.
   1702  1.1  christos                         else if (!strcmp(key, "Addreess") || !strcmp(key, "Address")) {
   1703  1.1  christos                             if (matched_address) {
   1704  1.1  christos                                 ERROR("prefixes array %zu: Address appears twice.", i);
   1705  1.1  christos                                 goto done_with_prefix_array;
   1706  1.1  christos                             }
   1707  1.1  christos                             destination = xpc_dictionary_get_string(array_sub_dict, "value");
   1708  1.1  christos                             if (destination == NULL) {
   1709  1.1  christos                                 INFO("null address");
   1710  1.1  christos                                 goto done_with_prefix_array;
   1711  1.1  christos                             }
   1712  1.1  christos                             matched_address = true;
   1713  1.1  christos                         } else if (!strcmp(key, "Metric")) {
   1714  1.1  christos                             if (matched_metric) {
   1715  1.1  christos                                 ERROR("prefixes array %zu: Metric appears twice.", i);
   1716  1.1  christos                                 goto done_with_prefix_array;
   1717  1.1  christos                             }
   1718  1.1  christos                             metric = (int)xpc_dictionary_get_uint64(array_sub_dict, "value");
   1719  1.1  christos                             matched_metric = true;
   1720  1.1  christos                         } else {
   1721  1.1  christos                             ERROR("Unknown key in prefix array %zu subdictionary %zu: " PUB_S_SRP, i, j, key);
   1722  1.1  christos                             goto done_with_prefix_array;
   1723  1.1  christos                         }
   1724  1.1  christos                         match_count++;
   1725  1.1  christos                     }
   1726  1.1  christos                 }
   1727  1.1  christos                 if (match_count != 2) {
   1728  1.1  christos                     ERROR("expecting %d sub-dictionaries to prefix array %zu, but got %d.",
   1729  1.1  christos                           2, i, match_count);
   1730  1.1  christos                     goto done_with_prefix_array;
   1731  1.1  christos                 }
   1732  1.1  christos 
   1733  1.1  christos                 // The prefix is in IPv6 address presentation form, so convert it to bits.
   1734  1.1  christos                 char prefix_buffer[INET6_ADDRSTRLEN];
   1735  1.1  christos                 const char *slash = strchr(destination, '/');
   1736  1.1  christos                 size_t prefix_pres_len = slash - destination;
   1737  1.1  christos                 if (prefix_pres_len >= INET6_ADDRSTRLEN - 1) {
   1738  1.1  christos                     ERROR("prefixes array %zu: destination is longer than maximum IPv6 address string: " PUB_S_SRP,
   1739  1.1  christos                           j, destination);
   1740  1.1  christos                     goto done_with_prefix_array;
   1741  1.1  christos                 }
   1742  1.1  christos #ifndef __clang_analyzer__ // destination is never null at this point
   1743  1.1  christos                 memcpy(prefix_buffer, destination, prefix_pres_len);
   1744  1.1  christos #endif
   1745  1.1  christos                 prefix_buffer[prefix_pres_len] = 0;
   1746  1.1  christos                 inet_pton(AF_INET6, prefix_buffer, &prefix_addr);
   1747  1.1  christos 
   1748  1.1  christos                 // Also convert the prefix.
   1749  1.1  christos                 char *endptr = NULL;
   1750  1.1  christos                 int prefix_len = (int)strtol(slash + 1, &endptr, 10);
   1751  1.1  christos                 if (endptr == slash + 1 || *endptr != 0 || prefix_len != 64) {
   1752  1.1  christos                     INFO("bogus prefix length provided by thread: " PUB_S_SRP, destination);
   1753  1.1  christos                     prefix_len = 64;
   1754  1.1  christos                 }
   1755  1.1  christos 
   1756  1.1  christos                 cti_prefix_t *prefix = cti_prefix_create(&prefix_addr, prefix_len, metric, 0, 0, false, false);
   1757  1.1  christos                 if (prefix != NULL) {
   1758  1.1  christos                     prefixes->prefixes[i] = prefix;
   1759  1.1  christos                 }
   1760  1.1  christos                 continue;
   1761  1.1  christos             done_with_prefix_array:
   1762  1.1  christos                 status = kCTIStatus_UnknownError;
   1763  1.1  christos             }
   1764  1.1  christos         }
   1765  1.1  christos     }
   1766  1.1  christos     if (status == kCTIStatus_NoError) {
   1767  1.1  christos         *vec_ret = prefixes;
   1768  1.1  christos     } else {
   1769  1.1  christos         if (prefixes != NULL) {
   1770  1.1  christos             RELEASE_HERE(prefixes, cti_prefix_vec);
   1771  1.1  christos         }
   1772  1.1  christos     }
   1773  1.1  christos     return status;
   1774  1.1  christos }
   1775  1.1  christos 
   1776  1.1  christos static void
   1777  1.1  christos cti_internal_prefix_reply_callback(cti_connection_t NONNULL conn_ref, object_t reply, cti_status_t status_in)
   1778  1.1  christos {
   1779  1.1  christos     cti_prefix_reply_t callback = conn_ref->callback.prefix_reply;
   1780  1.1  christos     cti_status_t status = status_in;
   1781  1.1  christos     cti_prefix_vec_t *vec = NULL;
   1782  1.1  christos     object_t result_dictionary = NULL;
   1783  1.1  christos     if (status == kCTIStatus_NoError) {
   1784  1.1  christos         status = cti_event_or_response_extract(reply, &result_dictionary);
   1785  1.1  christos         if (status == kCTIStatus_NoError) {
   1786  1.1  christos             object_t *value = xpc_dictionary_get_array(result_dictionary, "value");
   1787  1.1  christos             if (value == NULL) {
   1788  1.1  christos                 INFO("prefixes array not present in IPv6:Routes event.");
   1789  1.1  christos             } else {
   1790  1.1  christos                 status = cti_parse_prefixes_array(&vec, value);
   1791  1.1  christos             }
   1792  1.1  christos         }
   1793  1.1  christos     }
   1794  1.1  christos     if (callback != NULL) {
   1795  1.1  christos         INFO("[CX%d] calling callback for %p", conn_ref->serial, conn_ref);
   1796  1.1  christos         callback(conn_ref->context, vec, status);
   1797  1.1  christos     } else {
   1798  1.1  christos         INFO("[CX%d] Not calling callback.", conn_ref->serial);
   1799  1.1  christos     }
   1800  1.1  christos     if (vec != NULL) {
   1801  1.1  christos         RELEASE_HERE(vec, cti_prefix_vec);
   1802  1.1  christos     }
   1803  1.1  christos }
   1804  1.1  christos 
   1805  1.1  christos cti_status_t
   1806  1.1  christos cti_get_prefix_list_(srp_server_t *UNUSED server, cti_connection_t *ref, void *NULLABLE context,
   1807  1.1  christos                      cti_prefix_reply_t NONNULL callback, run_context_t NULLABLE client_queue,
   1808  1.1  christos                      const char *file, int line)
   1809  1.1  christos {
   1810  1.1  christos     cti_callback_t app_callback;
   1811  1.1  christos     app_callback.prefix_reply = callback;
   1812  1.1  christos     cti_status_t errx;
   1813  1.1  christos     object_t dict = xpc_dictionary_create(NULL, NULL, 0);
   1814  1.1  christos 
   1815  1.1  christos     xpc_dictionary_set_string(dict, "interface", "org.wpantund.v1");
   1816  1.1  christos     xpc_dictionary_set_string(dict, "path", "/org/wpantund/utun2");
   1817  1.1  christos     xpc_dictionary_set_string(dict, "method", "PropGet");
   1818  1.1  christos     xpc_dictionary_set_string(dict, "property_name", "IPv6:Routes");
   1819  1.1  christos 
   1820  1.1  christos     errx = setup_for_command(ref, client_queue, "get_prefix_list", "IPv6:Routes", NULL, dict, "WpanctlCmd",
   1821  1.1  christos                              context, app_callback, cti_internal_prefix_reply_callback, false, file, line);
   1822  1.1  christos     xpc_release(dict);
   1823  1.1  christos 
   1824  1.1  christos     return errx;
   1825  1.1  christos }
   1826  1.1  christos 
   1827  1.1  christos static void
   1828  1.1  christos cti_route_finalize(cti_route_t *route)
   1829  1.1  christos {
   1830  1.1  christos     free(route);
   1831  1.1  christos }
   1832  1.1  christos 
   1833  1.1  christos static void
   1834  1.1  christos cti_route_vec_finalize(cti_route_vec_t *routes)
   1835  1.1  christos {
   1836  1.1  christos     size_t i;
   1837  1.1  christos 
   1838  1.1  christos     if (routes->routes != NULL) {
   1839  1.1  christos         for (i = 0; i < routes->num; i++) {
   1840  1.1  christos             if (routes->routes[i] != NULL) {
   1841  1.1  christos                 RELEASE_HERE(routes->routes[i], cti_route);
   1842  1.1  christos             }
   1843  1.1  christos         }
   1844  1.1  christos         free(routes->routes);
   1845  1.1  christos     }
   1846  1.1  christos     free(routes);
   1847  1.1  christos }
   1848  1.1  christos 
   1849  1.1  christos cti_route_vec_t *
   1850  1.1  christos cti_route_vec_create_(size_t num_routes, const char *file, int line)
   1851  1.1  christos {
   1852  1.1  christos     cti_route_vec_t *routes = calloc(1, sizeof(*routes));
   1853  1.1  christos     if (routes != NULL) {
   1854  1.1  christos         if (num_routes != 0) {
   1855  1.1  christos             routes->routes = calloc(num_routes, sizeof(cti_route_t *));
   1856  1.1  christos             if (routes->routes == NULL) {
   1857  1.1  christos                 free(routes);
   1858  1.1  christos                 return NULL;
   1859  1.1  christos             }
   1860  1.1  christos         }
   1861  1.1  christos         routes->num = num_routes;
   1862  1.1  christos         RETAIN(routes, cti_route_vec);
   1863  1.1  christos     }
   1864  1.1  christos     return routes;
   1865  1.1  christos }
   1866  1.1  christos 
   1867  1.1  christos void
   1868  1.1  christos cti_route_vec_release_(cti_route_vec_t *routes, const char *file, int line)
   1869  1.1  christos {
   1870  1.1  christos     RELEASE(routes, cti_route_vec);
   1871  1.1  christos }
   1872  1.1  christos 
   1873  1.1  christos cti_route_t *
   1874  1.1  christos cti_route_create_(struct in6_addr *prefix, int prefix_length, offmesh_route_origin_t origin,
   1875  1.1  christos                   bool nat64, bool stable, offmesh_route_preference_t preference, int rloc,
   1876  1.1  christos                   bool next_hop_is_host, const char *file, int line)
   1877  1.1  christos {
   1878  1.1  christos     cti_route_t *route_ret = calloc(1, sizeof(*route_ret));
   1879  1.1  christos     if (prefix != NULL) {
   1880  1.1  christos         route_ret->prefix = *prefix;
   1881  1.1  christos         route_ret->prefix_length = prefix_length;
   1882  1.1  christos         route_ret->origin = origin;
   1883  1.1  christos         route_ret->nat64 = nat64;
   1884  1.1  christos         route_ret->stable = stable;
   1885  1.1  christos         route_ret->preference = preference;
   1886  1.1  christos         route_ret->rloc = rloc;
   1887  1.1  christos         route_ret->next_hop_is_host = next_hop_is_host;
   1888  1.1  christos         RETAIN(route_ret, cti_route);
   1889  1.1  christos     }
   1890  1.1  christos     return route_ret;
   1891  1.1  christos }
   1892  1.1  christos 
   1893  1.1  christos static cti_status_t
   1894  1.1  christos cti_parse_offmesh_routes_array(cti_route_vec_t **vec_ret, object_t routes_array)
   1895  1.1  christos {
   1896  1.1  christos     size_t i, j, routes_array_length = xpc_array_get_count(routes_array);
   1897  1.1  christos     cti_route_vec_t *routes = cti_route_vec_create(routes_array_length);
   1898  1.1  christos     cti_status_t status = kCTIStatus_NoError;
   1899  1.1  christos 
   1900  1.1  christos     if (routes == NULL) {
   1901  1.1  christos         INFO("no memory.");
   1902  1.1  christos         status = kCTIStatus_NoMemory;
   1903  1.1  christos     } else {
   1904  1.1  christos         // Array of arrays
   1905  1.1  christos         for (i = 0; i < routes_array_length; i++) {
   1906  1.1  christos             object_t route_array = xpc_array_get_value(routes_array, i);
   1907  1.1  christos             if (route_array == NULL) {
   1908  1.1  christos                 ERROR("Unable to get route array %zu", i);
   1909  1.1  christos             } else {
   1910  1.1  christos                 bool nat64 = false, stable = false, next_hop_is_host = false;
   1911  1.1  christos                 int rloc = 0, prefix_len = 0;
   1912  1.1  christos                 offmesh_route_preference_t pref = offmesh_route_preference_low;
   1913  1.1  christos                 offmesh_route_origin_t origin = offmesh_route_origin_user;
   1914  1.1  christos                 struct in6_addr prefix_addr = { };
   1915  1.1  christos                 size_t route_array_length = xpc_array_get_count(route_array);
   1916  1.1  christos                 for (j = 0; j < route_array_length; j++) {
   1917  1.1  christos                     object_t *array_sub_dict = xpc_array_get_value(route_array, j);
   1918  1.1  christos                     if (array_sub_dict == NULL) {
   1919  1.1  christos                         ERROR("can't get routes_array %zu subdictionary %zu", i, j);
   1920  1.1  christos                         goto done_with_route_array;
   1921  1.1  christos                     }
   1922  1.1  christos                     const char *key = xpc_dictionary_get_string(array_sub_dict, "key");
   1923  1.1  christos                     if (key == NULL) {
   1924  1.1  christos                         ERROR("Invalid routes array %zu subdictionary %zu: no key", i, j);
   1925  1.1  christos                         goto done_with_route_array;
   1926  1.1  christos                     } else if (!strcmp(key, "nat64")) {
   1927  1.1  christos                         if (xpc_dictionary_get_uint64(array_sub_dict, "value") == 1) {
   1928  1.1  christos                             nat64 = true;
   1929  1.1  christos                         }
   1930  1.1  christos                     } else if (!strcmp(key, "origin")) {
   1931  1.1  christos                         const char *origin_str = xpc_dictionary_get_string(array_sub_dict, "value");
   1932  1.1  christos                         if (origin_str == NULL) {
   1933  1.1  christos                             ERROR("Invalid routes array %zu subdictionary %zu: NULL origin", i, j);
   1934  1.1  christos                             goto done_with_route_array;
   1935  1.1  christos                         }
   1936  1.1  christos                         if (!strcmp(origin_str, "ncp")) {
   1937  1.1  christos                             origin = offmesh_route_origin_ncp;
   1938  1.1  christos                         }
   1939  1.1  christos                     } else if (!strcmp(key, "stable")) {
   1940  1.1  christos                         if (xpc_dictionary_get_uint64(array_sub_dict, "value") == 1) {
   1941  1.1  christos                             stable = true;
   1942  1.1  christos                         }
   1943  1.1  christos                     } else if (!strcmp(key, "nextHopIsHost")) {
   1944  1.1  christos                         if (xpc_dictionary_get_bool(array_sub_dict, "value") == true) {
   1945  1.1  christos                             next_hop_is_host = true;
   1946  1.1  christos                         }
   1947  1.1  christos                     } else if (!strcmp(key, "rloc")) {
   1948  1.1  christos                         rloc = (int)xpc_dictionary_get_uint64(array_sub_dict, "value");
   1949  1.1  christos                     } else if (!strcmp(key, "preference")) {
   1950  1.1  christos                         const char *pref_str = xpc_dictionary_get_string(array_sub_dict, "value");
   1951  1.1  christos                         if (pref_str == NULL) {
   1952  1.1  christos                             ERROR("Invalid routes array %zu subdictionary %zu: NULL preference", i, j);
   1953  1.1  christos                             goto done_with_route_array;
   1954  1.1  christos                         }
   1955  1.1  christos                         if (!strcmp(pref_str, "high")) {
   1956  1.1  christos                             pref = offmesh_route_preference_high;
   1957  1.1  christos                         } else if (!strcmp(pref_str, "medium")) {
   1958  1.1  christos                             pref = offmesh_route_preference_medium;
   1959  1.1  christos                         }
   1960  1.1  christos                     } else if (!strcmp(key, "address")) {
   1961  1.1  christos                         const char *addr_str = xpc_dictionary_get_string(array_sub_dict, "value");
   1962  1.1  christos                         if (addr_str == NULL) {
   1963  1.1  christos                             ERROR("Invalid routes array %zu subdictionary %zu: NULL address", i, j);
   1964  1.1  christos                             goto done_with_route_array;
   1965  1.1  christos                         }
   1966  1.1  christos                         // The prefix is in IPv6 address presentation form, so convert it to bits.
   1967  1.1  christos                         char prefix_buffer[INET6_ADDRSTRLEN];
   1968  1.1  christos                         const char *slash = strchr(addr_str, '/');
   1969  1.1  christos                         if (slash == NULL) {
   1970  1.1  christos                             ERROR("bad address " PRI_S_SRP, addr_str);
   1971  1.1  christos                             goto done_with_route_array;
   1972  1.1  christos                         }
   1973  1.1  christos                         size_t prefix_pres_len = slash - addr_str;
   1974  1.1  christos                         if (prefix_pres_len > INET6_ADDRSTRLEN - 1) {
   1975  1.1  christos                             ERROR("routes array %zu: destination is longer than maximum IPv6 address string: " PRI_S_SRP,
   1976  1.1  christos                                  i, addr_str);
   1977  1.1  christos                                 goto done_with_route_array;
   1978  1.1  christos                         }
   1979  1.1  christos #ifndef __clang_analyzer__ // destination is never null at this point
   1980  1.1  christos                         memcpy(prefix_buffer, addr_str, prefix_pres_len);
   1981  1.1  christos #endif
   1982  1.1  christos                         prefix_buffer[prefix_pres_len] = 0;
   1983  1.1  christos                         if (inet_pton(AF_INET6, prefix_buffer, &prefix_addr) != 1) {
   1984  1.1  christos                             ERROR("invalid ipv6 address " PRI_S_SRP, prefix_buffer);
   1985  1.1  christos                             goto done_with_route_array;
   1986  1.1  christos                         }
   1987  1.1  christos                         // Convert the prefix.
   1988  1.1  christos                         char *endptr = NULL;
   1989  1.1  christos                         long tmp = strtol(slash + 1, &endptr, 10);
   1990  1.1  christos                         if (endptr == slash + 1 || *endptr != 0 || tmp < 0 || tmp > 128) {
   1991  1.1  christos                             ERROR("bogus ipv6 prefix length provided by thread: " PRI_S_SRP, addr_str);
   1992  1.1  christos                             goto done_with_route_array;
   1993  1.1  christos                         } else {
   1994  1.1  christos                             prefix_len = (int)tmp;
   1995  1.1  christos                         }
   1996  1.1  christos                     } else {
   1997  1.1  christos                         ERROR("Invalid routes array %zu subdictionary %zu: unknown key " PUB_S_SRP, i, j, key);
   1998  1.1  christos                         goto done_with_route_array;
   1999  1.1  christos                     }
   2000  1.1  christos                 }
   2001  1.1  christos                 cti_route_t *r = cti_route_create(&prefix_addr, prefix_len, origin, nat64, stable, pref, rloc, next_hop_is_host);
   2002  1.1  christos                 if (r == NULL) {
   2003  1.1  christos                     ERROR("No memory when create route.");
   2004  1.1  christos                     goto done_with_route_array;
   2005  1.1  christos                 } else {
   2006  1.1  christos                     SEGMENTED_IPv6_ADDR_GEN_SRP(prefix_addr.s6_addr, prefix_buf);
   2007  1.1  christos                     INFO("Got offmesh route " PRI_SEGMENTED_IPv6_ADDR_SRP " len %d origin:" PUB_S_SRP " nat64:"
   2008  1.1  christos                          PUB_S_SRP " stable:" PUB_S_SRP " preference:%d rloc:0x%04x next_hop_is_host:" PUB_S_SRP,
   2009  1.1  christos                          SEGMENTED_IPv6_ADDR_PARAM_SRP(prefix_addr.s6_addr, prefix_buf), prefix_len,
   2010  1.1  christos                          origin == offmesh_route_origin_user ? "user":"ncp", nat64 ? "yes" : "no",
   2011  1.1  christos                          stable ? "yes" : "no", pref, rloc, next_hop_is_host ? "yes" : "no");
   2012  1.1  christos                          routes->routes[i] = r;
   2013  1.1  christos                 }
   2014  1.1  christos                 continue;
   2015  1.1  christos             done_with_route_array:
   2016  1.1  christos                 status = kCTIStatus_UnknownError;
   2017  1.1  christos                 break;
   2018  1.1  christos             }
   2019  1.1  christos         }
   2020  1.1  christos     }
   2021  1.1  christos     if (status == kCTIStatus_NoError) {
   2022  1.1  christos         *vec_ret = routes;
   2023  1.1  christos     } else {
   2024  1.1  christos         if (routes != NULL) {
   2025  1.1  christos             RELEASE_HERE(routes, cti_route_vec);
   2026  1.1  christos         }
   2027  1.1  christos     }
   2028  1.1  christos     return status;
   2029  1.1  christos }
   2030  1.1  christos 
   2031  1.1  christos static void
   2032  1.1  christos cti_internal_offmesh_route_reply_callback(cti_connection_t NONNULL conn_ref, object_t reply, cti_status_t status_in)
   2033  1.1  christos {
   2034  1.1  christos     cti_offmesh_route_reply_t callback = conn_ref->callback.offmesh_route_reply;
   2035  1.1  christos     cti_status_t status = status_in;
   2036  1.1  christos     cti_route_vec_t *vec = NULL;
   2037  1.1  christos     object_t result_dictionary = NULL;
   2038  1.1  christos     if (status == kCTIStatus_NoError) {
   2039  1.1  christos         status = cti_event_or_response_extract(reply, &result_dictionary);
   2040  1.1  christos         if (status == kCTIStatus_NoError) {
   2041  1.1  christos             object_t *value = xpc_dictionary_get_array(result_dictionary, "value");
   2042  1.1  christos             if (value == NULL) {
   2043  1.1  christos                 INFO("offmesh route array not present in Thread:OffMeshroutes event.");
   2044  1.1  christos             } else {
   2045  1.1  christos                 status = cti_parse_offmesh_routes_array(&vec, value);
   2046  1.1  christos             }
   2047  1.1  christos         }
   2048  1.1  christos     }
   2049  1.1  christos     if (callback != NULL) {
   2050  1.1  christos         INFO("[CX%d] calling callback for %p", conn_ref->serial, conn_ref);
   2051  1.1  christos         callback(conn_ref->context, vec, status);
   2052  1.1  christos     } else {
   2053  1.1  christos         INFO("[CX%d] Not calling callback for %p", conn_ref->serial, conn_ref);
   2054  1.1  christos     }
   2055  1.1  christos     if (vec != NULL) {
   2056  1.1  christos         RELEASE_HERE(vec, cti_route_vec);
   2057  1.1  christos     }
   2058  1.1  christos }
   2059  1.1  christos 
   2060  1.1  christos cti_status_t
   2061  1.1  christos cti_get_offmesh_route_list_(srp_server_t *UNUSED server, cti_connection_t *ref,
   2062  1.1  christos                             void *NULLABLE context, cti_offmesh_route_reply_t NONNULL callback,
   2063  1.1  christos                             run_context_t NULLABLE client_queue, const char *file, int line)
   2064  1.1  christos {
   2065  1.1  christos     cti_callback_t app_callback;
   2066  1.1  christos     app_callback.offmesh_route_reply = callback;
   2067  1.1  christos     cti_status_t errx;
   2068  1.1  christos     object_t dict = xpc_dictionary_create(NULL, NULL, 0);
   2069  1.1  christos 
   2070  1.1  christos     xpc_dictionary_set_string(dict, "interface", "org.wpantund.v1");
   2071  1.1  christos     xpc_dictionary_set_string(dict, "path", "/org/wpantund/utun2");
   2072  1.1  christos     xpc_dictionary_set_string(dict, "method", "PropGet");
   2073  1.1  christos     xpc_dictionary_set_string(dict, "property_name", "Thread:OffMeshroutes");
   2074  1.1  christos 
   2075  1.1  christos     errx = setup_for_command(ref, client_queue, "get_offmesh_route_list", "Thread:OffMeshroutes", NULL, dict, "WpanctlCmd",
   2076  1.1  christos                              context, app_callback, cti_internal_offmesh_route_reply_callback, false, file, line);
   2077  1.1  christos     INFO("get_offmesh_route_list result %d", errx);
   2078  1.1  christos     xpc_release(dict);
   2079  1.1  christos 
   2080  1.1  christos     return errx;
   2081  1.1  christos }
   2082  1.1  christos 
   2083  1.1  christos static cti_status_t
   2084  1.1  christos cti_parse_onmesh_prefix_array(cti_prefix_vec_t **vec_ret, object_t prefix_array)
   2085  1.1  christos {
   2086  1.1  christos     size_t i, prefix_array_length = xpc_array_get_count(prefix_array);
   2087  1.1  christos     cti_prefix_vec_t *prefixes = cti_prefix_vec_create(prefix_array_length);
   2088  1.1  christos     cti_status_t status = kCTIStatus_NoError;
   2089  1.1  christos 
   2090  1.1  christos     if (prefixes == NULL) {
   2091  1.1  christos         INFO("no memory.");
   2092  1.1  christos         status = kCTIStatus_NoMemory;
   2093  1.1  christos     } else {
   2094  1.1  christos         for (i = 0; i < prefix_array_length; i++) {
   2095  1.1  christos             const char *route = xpc_array_get_string(prefix_array, i);
   2096  1.1  christos             bool stable = false;
   2097  1.1  christos             int rloc = 0, prefix_len = 0, flags = 0;
   2098  1.1  christos             bool ncp = false;
   2099  1.1  christos             struct in6_addr prefix_addr = { };
   2100  1.1  christos 
   2101  1.1  christos             char *dup = strdup(route);
   2102  1.1  christos             if (dup == NULL) {
   2103  1.1  christos                 INFO("no memory when strdup");
   2104  1.1  christos                 goto done_with_prefix_array;
   2105  1.1  christos             }
   2106  1.1  christos             char *token, *remain = dup;
   2107  1.1  christos             int index = 0;
   2108  1.1  christos             while ((token = strtok_r(remain, " ", &remain))) {
   2109  1.1  christos                 if (index == 0) {
   2110  1.1  christos                     if (!inet_pton(AF_INET6, token, &prefix_addr)) {
   2111  1.1  christos                         ERROR("invalid ipv6 address " PUB_S_SRP, token);
   2112  1.1  christos                         goto done_with_prefix_array;
   2113  1.1  christos                     }
   2114  1.1  christos                 } else if (index == 1) {
   2115  1.1  christos                     if (strncmp(token, "prefix_len:", 11)) {
   2116  1.1  christos                         ERROR("expecting prefix_len rather than " PRI_S_SRP " at %d", token, index);
   2117  1.1  christos                         goto done_with_prefix_array;
   2118  1.1  christos                     }
   2119  1.1  christos                     char *endptr = NULL;
   2120  1.1  christos                     prefix_len = (int)strtol(token + 11, &endptr, 10);
   2121  1.1  christos                 } else if (index == 2) {
   2122  1.1  christos                     if (strncmp(token, "origin:", 7)) {
   2123  1.1  christos                         ERROR("expecting origin rather than " PRI_S_SRP " at %d", token, index);
   2124  1.1  christos                         goto done_with_prefix_array;
   2125  1.1  christos                     }
   2126  1.1  christos                     if (!strcmp(token + 7, "ncp")){
   2127  1.1  christos                         ncp = true;
   2128  1.1  christos                     } else if (strcmp(token + 7, "user")) {
   2129  1.1  christos                         ERROR("unexpected origin: " PRI_S_SRP, token + 7);
   2130  1.1  christos                         goto done_with_prefix_array;
   2131  1.1  christos                     }
   2132  1.1  christos                 } else if (index == 3) {
   2133  1.1  christos                     if (strncmp(token, "stable:", 7)) {
   2134  1.1  christos                         ERROR("expecting table rather than " PRI_S_SRP " at %d", token, index);
   2135  1.1  christos                         goto done_with_prefix_array;
   2136  1.1  christos                     }
   2137  1.1  christos                     if (!strcmp(token + 7, "yes")){
   2138  1.1  christos                         stable = true;
   2139  1.1  christos                     } else if (strcmp(token + 7, "no")) {
   2140  1.1  christos                         ERROR("unexpected boolean state: " PRI_S_SRP, token + 7);
   2141  1.1  christos                         goto done_with_prefix_array;
   2142  1.1  christos                     }
   2143  1.1  christos                 } else if (index == 4) {
   2144  1.1  christos                     if (strncmp(token, "flags:", 6)) {
   2145  1.1  christos                         ERROR("expecting flags rather than " PRI_S_SRP " at %d", token, index);
   2146  1.1  christos                         goto done_with_prefix_array;
   2147  1.1  christos                     }
   2148  1.1  christos                     char *endptr = NULL;
   2149  1.1  christos                     flags = ntohs((int)strtol(token + 6, &endptr, 16));
   2150  1.1  christos                 } else if (index == 14) {
   2151  1.1  christos                     if (strncmp(token, "rloc:", 5)) {
   2152  1.1  christos                         ERROR("expecting rloc rather than " PRI_S_SRP " at %d", token, index);
   2153  1.1  christos                         goto done_with_prefix_array;
   2154  1.1  christos                     }
   2155  1.1  christos                     char *endptr = NULL;
   2156  1.1  christos                     rloc = (int)strtol(token + 5, &endptr, 16);
   2157  1.1  christos                 } else {
   2158  1.1  christos                     // Anything else is in the parsed flags, which we don't care about, or is a bogon, which will probably result
   2159  1.1  christos                     // in a failure elsewhere. We're not really expecting bogons, so putting in extra code here to flag them would
   2160  1.1  christos                     // be useless.
   2161  1.1  christos                 }
   2162  1.1  christos                 index++;
   2163  1.1  christos             }
   2164  1.1  christos             SEGMENTED_IPv6_ADDR_GEN_SRP(prefix_addr.s6_addr, prefix_buf);
   2165  1.1  christos             INFO("got prefix " PRI_SEGMENTED_IPv6_ADDR_SRP " len %d origin:" PUB_S_SRP " stable:" PUB_S_SRP " flags:%x rloc:%04x",
   2166  1.1  christos                  SEGMENTED_IPv6_ADDR_PARAM_SRP(prefix_addr.s6_addr, prefix_buf), prefix_len,
   2167  1.1  christos                  ncp ? "ncp" : "user", stable ? "yes" : "no", flags, rloc);
   2168  1.1  christos             cti_prefix_t *r = cti_prefix_create(&prefix_addr, prefix_len, 0, flags, rloc, stable, ncp);
   2169  1.1  christos             if (r) {
   2170  1.1  christos                 prefixes->prefixes[i] = r;
   2171  1.1  christos             } else {
   2172  1.1  christos                 ERROR("no memory for parsed prefix!");
   2173  1.1  christos                 goto done_with_prefix_array;
   2174  1.1  christos             }
   2175  1.1  christos             free(dup);
   2176  1.1  christos             continue;
   2177  1.1  christos         done_with_prefix_array:
   2178  1.1  christos             status = kCTIStatus_UnknownError;
   2179  1.1  christos             free(dup);
   2180  1.1  christos             break;
   2181  1.1  christos         }
   2182  1.1  christos     }
   2183  1.1  christos     if (status == kCTIStatus_NoError) {
   2184  1.1  christos         *vec_ret = prefixes;
   2185  1.1  christos     } else {
   2186  1.1  christos         if (prefixes != NULL) {
   2187  1.1  christos             RELEASE_HERE(prefixes, cti_prefix_vec);
   2188  1.1  christos         }
   2189  1.1  christos     }
   2190  1.1  christos     return status;
   2191  1.1  christos }
   2192  1.1  christos 
   2193  1.1  christos static void
   2194  1.1  christos cti_internal_onmesh_prefix_reply_callback(cti_connection_t NONNULL conn_ref, object_t reply, cti_status_t status_in)
   2195  1.1  christos {
   2196  1.1  christos     cti_onmesh_prefix_reply_t callback = conn_ref->callback.onmesh_prefix_reply;
   2197  1.1  christos     cti_status_t status = status_in;
   2198  1.1  christos     cti_prefix_vec_t *vec = NULL;
   2199  1.1  christos     object_t result_dictionary = NULL;
   2200  1.1  christos     if (status == kCTIStatus_NoError) {
   2201  1.1  christos         status = cti_event_or_response_extract(reply, &result_dictionary);
   2202  1.1  christos         if (status == kCTIStatus_NoError) {
   2203  1.1  christos             object_t *value = xpc_dictionary_get_array(result_dictionary, "value");
   2204  1.1  christos             if (value == NULL) {
   2205  1.1  christos                 INFO("onmesh prefix array not present in Thread:OnMeshPrefixes event.");
   2206  1.1  christos             } else {
   2207  1.1  christos                 status = cti_parse_onmesh_prefix_array(&vec, value);
   2208  1.1  christos             }
   2209  1.1  christos         }
   2210  1.1  christos     }
   2211  1.1  christos     if (callback != NULL) {
   2212  1.1  christos         INFO("[CX%d] calling callback for %p", conn_ref->serial, conn_ref);
   2213  1.1  christos         callback(conn_ref->context, vec, status);
   2214  1.1  christos     } else {
   2215  1.1  christos         INFO("[CX%d] not calling callback for %p", conn_ref->serial, conn_ref);
   2216  1.1  christos     }
   2217  1.1  christos     if (vec != NULL) {
   2218  1.1  christos         RELEASE_HERE(vec, cti_prefix_vec);
   2219  1.1  christos     }
   2220  1.1  christos }
   2221  1.1  christos 
   2222  1.1  christos cti_status_t
   2223  1.1  christos cti_get_onmesh_prefix_list_(srp_server_t *UNUSED server, cti_connection_t NULLABLE *NULLABLE ref,
   2224  1.1  christos                             void *NULLABLE context, cti_onmesh_prefix_reply_t NONNULL callback,
   2225  1.1  christos                             run_context_t NULLABLE client_queue, const char *file, int line)
   2226  1.1  christos {
   2227  1.1  christos     cti_callback_t app_callback;
   2228  1.1  christos     app_callback.onmesh_prefix_reply = callback;
   2229  1.1  christos     cti_status_t errx;
   2230  1.1  christos     object_t dict = xpc_dictionary_create(NULL, NULL, 0);
   2231  1.1  christos 
   2232  1.1  christos     xpc_dictionary_set_string(dict, "interface", "org.wpantund.v1");
   2233  1.1  christos     xpc_dictionary_set_string(dict, "path", "/org/wpantund/utun2");
   2234  1.1  christos     xpc_dictionary_set_string(dict, "method", "PropGet");
   2235  1.1  christos     xpc_dictionary_set_string(dict, "property_name", "Thread:OnMeshPrefixes");
   2236  1.1  christos 
   2237  1.1  christos     errx = setup_for_command(ref, client_queue, "get_onmesh_prefix_list", "Thread:OnMeshPrefixes", NULL, dict,
   2238  1.1  christos                              "WpanctlCmd", context, app_callback, cti_internal_onmesh_prefix_reply_callback, false,
   2239  1.1  christos                              file, line);
   2240  1.1  christos     INFO("result %d", errx);
   2241  1.1  christos     xpc_release(dict);
   2242  1.1  christos 
   2243  1.1  christos     return errx;
   2244  1.1  christos }
   2245  1.1  christos 
   2246  1.1  christos static void
   2247  1.1  christos cti_internal_rloc16_reply_callback(cti_connection_t NONNULL conn_ref, object_t reply, cti_status_t status_in)
   2248  1.1  christos {
   2249  1.1  christos     cti_rloc16_reply_t callback = conn_ref->callback.rloc16_reply;
   2250  1.1  christos     cti_status_t status = status_in;
   2251  1.1  christos     object_t result_dictionary = NULL;
   2252  1.1  christos     uint16_t rloc16 = 0;
   2253  1.1  christos     if (status == kCTIStatus_NoError) {
   2254  1.1  christos         status = cti_event_or_response_extract(reply, &result_dictionary);
   2255  1.1  christos         // The reply format is
   2256  1.1  christos         // commandData:  {response: "> e000
   2257  1.1  christos         // Done", method: "OtCtlCmd"}
   2258  1.1  christos         // where e000 is the rloc16 that we want to extract
   2259  1.1  christos         if (status == kCTIStatus_NoError) {
   2260  1.1  christos             const char *rloc16_str = xpc_dictionary_get_string(result_dictionary, "response");
   2261  1.1  christos             char *endptr = "\n";
   2262  1.1  christos             rloc16 = (uint16_t)strtol(&rloc16_str[2], &endptr, 16);
   2263  1.1  christos         }
   2264  1.1  christos     }
   2265  1.1  christos     if (callback != NULL) {
   2266  1.1  christos         INFO("[CX%d] calling callback for %p", conn_ref->serial, conn_ref);
   2267  1.1  christos         callback(conn_ref->context, rloc16, status);
   2268  1.1  christos     } else {
   2269  1.1  christos         INFO("[CX%d] not calling callback for %p", conn_ref->serial, conn_ref);
   2270  1.1  christos     }
   2271  1.1  christos }
   2272  1.1  christos 
   2273  1.1  christos cti_status_t
   2274  1.1  christos cti_get_rloc16_(srp_server_t *UNUSED server, cti_connection_t *ref,
   2275  1.1  christos                 void *NULLABLE context, cti_rloc16_reply_t NONNULL callback,
   2276  1.1  christos                 run_context_t NULLABLE client_queue, const char *file, int line)
   2277  1.1  christos {
   2278  1.1  christos     cti_callback_t app_callback;
   2279  1.1  christos     app_callback.rloc16_reply = callback;
   2280  1.1  christos     cti_status_t errx;
   2281  1.1  christos     object_t dict = xpc_dictionary_create(NULL, NULL, 0);
   2282  1.1  christos 
   2283  1.1  christos     xpc_dictionary_set_string(dict, "interface", "org.wpantund.v1");
   2284  1.1  christos     xpc_dictionary_set_string(dict, "path", "/org/wpantund/utun2");
   2285  1.1  christos     xpc_dictionary_set_string(dict, "method", "OtCtlCmd");
   2286  1.1  christos     xpc_dictionary_set_string(dict, "otctl_cmd", "rloc16");
   2287  1.1  christos 
   2288  1.1  christos     errx = setup_for_command(ref, client_queue, "get_rloc16", NULL, NULL, dict, "WpanctlCmd",
   2289  1.1  christos                              context, app_callback, cti_internal_rloc16_reply_callback, false, file, line);
   2290  1.1  christos     INFO("get_rloc16 result %d", errx);
   2291  1.1  christos     xpc_release(dict);
   2292  1.1  christos 
   2293  1.1  christos     return errx;
   2294  1.1  christos }
   2295  1.1  christos 
   2296  1.1  christos static void
   2297  1.1  christos cti_internal_wed_reply_callback(cti_connection_t NONNULL conn_ref, object_t reply, cti_status_t status_in)
   2298  1.1  christos {
   2299  1.1  christos     cti_wed_reply_t callback = conn_ref->callback.wed_reply;
   2300  1.1  christos     cti_service_vec_t *vec = NULL;
   2301  1.1  christos     cti_status_t status = status_in;
   2302  1.1  christos 
   2303  1.1  christos     const char *extended_mac = NULL;
   2304  1.1  christos     const char *ml_eid = NULL;
   2305  1.1  christos     bool added = false;
   2306  1.1  christos 
   2307  1.1  christos     if (status == kCTIStatus_NoError) {
   2308  1.1  christos         object_t result_dictionary = NULL;
   2309  1.1  christos         status = cti_event_or_response_extract(reply, &result_dictionary);
   2310  1.1  christos         if (status == kCTIStatus_NoError) {
   2311  1.1  christos             object_t *value_array = xpc_dictionary_get_array(result_dictionary, "value");
   2312  1.1  christos             if (value_array == NULL) {
   2313  1.1  christos                 INFO("[CX%d] wed status array not present in wed status event.", conn_ref->serial);
   2314  1.1  christos                 goto out;
   2315  1.1  christos             } else {
   2316  1.1  christos                 size_t value_array_length = xpc_array_get_count(value_array);
   2317  1.1  christos                 for (size_t i = 0; i < value_array_length; i++) {
   2318  1.1  christos                     object_t *elt = xpc_array_get_value(value_array, i);
   2319  1.1  christos                     xpc_type_t type = xpc_get_type(elt);
   2320  1.1  christos                     if (type != XPC_TYPE_DICTIONARY) {
   2321  1.1  christos                         ERROR("non-dictionary element of value array");
   2322  1.1  christos                         goto out;
   2323  1.1  christos                     }
   2324  1.1  christos                     const char *key = xpc_dictionary_get_string(elt, "key");
   2325  1.1  christos                     if (key == NULL) {
   2326  1.1  christos                         ERROR("no key in value array");
   2327  1.1  christos                         goto out;
   2328  1.1  christos                     }
   2329  1.1  christos                     const char *value = xpc_dictionary_get_string(elt, "value");
   2330  1.1  christos                     if (!strcmp(key, "extendedMACAddress")) {
   2331  1.1  christos                         extended_mac = value;
   2332  1.1  christos                     } else if (!strcmp(key, "mleid")) {
   2333  1.1  christos                         ml_eid = value;
   2334  1.1  christos                     } else if (!strcmp(key, "status")) {
   2335  1.1  christos                         if (!strcmp(value, "wed_added")) {
   2336  1.1  christos                             added = true;
   2337  1.1  christos                         } else if (!strcmp(value, "wed_removed")) {
   2338  1.1  christos                             added = false;
   2339  1.1  christos                         } else {
   2340  1.1  christos                             ERROR("unknown wed status " PUB_S_SRP, value);
   2341  1.1  christos                             goto out;
   2342  1.1  christos                         }
   2343  1.1  christos                     } else {
   2344  1.1  christos                         if (value == NULL) {
   2345  1.1  christos                             INFO("unknown key in response: " PUB_S_SRP, key);
   2346  1.1  christos                         } else {
   2347  1.1  christos                             INFO("unknown key " PUB_S_SRP " with value " PRI_S_SRP, key, value);
   2348  1.1  christos                         }
   2349  1.1  christos                     }
   2350  1.1  christos                 }
   2351  1.1  christos             }
   2352  1.1  christos         }
   2353  1.1  christos     }
   2354  1.1  christos out:
   2355  1.1  christos     if (callback != NULL) {
   2356  1.1  christos         if (added && (ml_eid == NULL || extended_mac == NULL)) {
   2357  1.1  christos             added = false;
   2358  1.1  christos         }
   2359  1.1  christos         INFO("[CX%d] calling callback for %p", conn_ref->serial, conn_ref);
   2360  1.1  christos         callback(conn_ref->context, extended_mac, ml_eid, added, status);
   2361  1.1  christos     }
   2362  1.1  christos     if (vec != NULL) {
   2363  1.1  christos         RELEASE_HERE(vec, cti_service_vec);
   2364  1.1  christos     }
   2365  1.1  christos }
   2366  1.1  christos 
   2367  1.1  christos cti_status_t
   2368  1.1  christos cti_track_wed_status_(srp_server_t *UNUSED server, cti_connection_t *ref, void *NULLABLE context,
   2369  1.1  christos                       cti_wed_reply_t NONNULL callback, run_context_t NULLABLE client_queue,
   2370  1.1  christos                       const char *file, int line)
   2371  1.1  christos {
   2372  1.1  christos     cti_callback_t app_callback;
   2373  1.1  christos     app_callback.wed_reply = callback;
   2374  1.1  christos     cti_status_t errx;
   2375  1.1  christos     object_t dict = xpc_dictionary_create(NULL, NULL, 0);
   2376  1.1  christos 
   2377  1.1  christos     xpc_dictionary_set_string(dict, "interface", "org.wpantund.v1");
   2378  1.1  christos     xpc_dictionary_set_string(dict, "path", "/org/wpantund/utun2");
   2379  1.1  christos     xpc_dictionary_set_string(dict, "method", "PropGet");
   2380  1.1  christos     xpc_dictionary_set_string(dict, "property_name", "WakeOnDeviceConnectionStatus");
   2381  1.1  christos 
   2382  1.1  christos     errx = setup_for_command(ref, client_queue, "get_wed_status", "WakeOnDeviceConnectionStatus", NULL, dict, "WpanctlCmd",
   2383  1.1  christos                              context, app_callback, cti_internal_wed_reply_callback, false, file, line);
   2384  1.1  christos     xpc_release(dict);
   2385  1.1  christos 
   2386  1.1  christos     return errx;
   2387  1.1  christos }
   2388  1.1  christos 
   2389  1.1  christos cti_status_t
   2390  1.1  christos cti_track_neighbor_ml_eid_(srp_server_t *UNUSED server, cti_connection_t *ref, void *NULLABLE context,
   2391  1.1  christos                            cti_string_property_reply_t NONNULL callback, run_context_t NULLABLE client_queue,
   2392  1.1  christos                            const char *file, int line)
   2393  1.1  christos {
   2394  1.1  christos     cti_callback_t app_callback;
   2395  1.1  christos     app_callback.string_property_reply = callback;
   2396  1.1  christos     cti_status_t errx;
   2397  1.1  christos     object_t dict = xpc_dictionary_create(NULL, NULL, 0);
   2398  1.1  christos 
   2399  1.1  christos     xpc_dictionary_set_string(dict, "interface", "org.wpantund.v1");
   2400  1.1  christos     xpc_dictionary_set_string(dict, "path", "/org/wpantund/utun2");
   2401  1.1  christos     xpc_dictionary_set_string(dict, "method", "PropGet");
   2402  1.1  christos     xpc_dictionary_set_string(dict, "property_name", "ThreadNeighborMeshLocalAddress");
   2403  1.1  christos 
   2404  1.1  christos     errx = setup_for_command(ref, client_queue, "get_neighbor_ml_eid", "ThreadNeighborMeshLocalAddress", "ThreadNeighborMeshLocalAddress", dict, "WpanctlCmd",
   2405  1.1  christos                              context, app_callback, cti_internal_string_event_reply, false, file, line);
   2406  1.1  christos     xpc_release(dict);
   2407  1.1  christos 
   2408  1.1  christos     return errx;
   2409  1.1  christos }
   2410  1.1  christos 
   2411  1.1  christos cti_status_t
   2412  1.1  christos cti_add_ml_eid_mapping_(srp_server_t *UNUSED server, void *NULLABLE context,
   2413  1.1  christos                         cti_reply_t NONNULL callback, run_context_t NULLABLE client_queue,
   2414  1.1  christos                         struct in6_addr *omr_addr, struct in6_addr *ml_eid, const char *hostname,
   2415  1.1  christos                         const char *file, int line)
   2416  1.1  christos {
   2417  1.1  christos     cti_callback_t app_callback;
   2418  1.1  christos     app_callback.reply = callback;
   2419  1.1  christos     cti_status_t errx;
   2420  1.1  christos     object_t dict = xpc_dictionary_create(NULL, NULL, 0);
   2421  1.1  christos     char addrbuf[INET6_ADDRSTRLEN];
   2422  1.1  christos 
   2423  1.1  christos     xpc_dictionary_set_string(dict, "interface", "org.wpantund.v1");
   2424  1.1  christos     xpc_dictionary_set_string(dict, "path", "/org/wpantund/utun2");
   2425  1.1  christos     xpc_dictionary_set_string(dict, "method", "UpdateAccessoryData");
   2426  1.1  christos     inet_ntop(AF_INET6, omr_addr, addrbuf, sizeof(addrbuf));
   2427  1.1  christos     xpc_dictionary_set_string(dict, "ipaddr_add", addrbuf);
   2428  1.1  christos     inet_ntop(AF_INET6, ml_eid, addrbuf, sizeof(addrbuf));
   2429  1.1  christos     xpc_dictionary_set_string(dict, "ipaddr_lookup", addrbuf);
   2430  1.1  christos     xpc_dictionary_set_string(dict, "host_info", hostname);
   2431  1.1  christos 
   2432  1.1  christos     errx = setup_for_command(NULL, client_queue, "add_mle_mapping", NULL, NULL, dict, "WpanctlCmd",
   2433  1.1  christos                              context, app_callback, cti_internal_reply_callback, false, file, line);
   2434  1.1  christos     xpc_release(dict);
   2435  1.1  christos 
   2436  1.1  christos     return errx;
   2437  1.1  christos }
   2438  1.1  christos 
   2439  1.1  christos 
   2440  1.1  christos cti_status_t
   2441  1.1  christos cti_events_discontinue(cti_connection_t ref)
   2442  1.1  christos {
   2443  1.1  christos     if (ref->connection != NULL) {
   2444  1.1  christos         INFO("[CX%d] canceling connection %p", ref->serial, ref->connection);
   2445  1.1  christos         xpc_connection_cancel(ref->connection);
   2446  1.1  christos     }
   2447  1.1  christos     // This is releasing the caller's reference.
   2448  1.1  christos     cti_connection_release(ref);
   2449  1.1  christos     return kCTIStatus_NoError;
   2450  1.1  christos }
   2451  1.1  christos 
   2452  1.1  christos // Local Variables:
   2453  1.1  christos // mode: C
   2454  1.1  christos // tab-width: 4
   2455  1.1  christos // c-file-style: "bsd"
   2456  1.1  christos // c-basic-offset: 4
   2457  1.1  christos // fill-column: 120
   2458  1.1  christos // indent-tabs-mode: nil
   2459  1.1  christos // End:
   2460