]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/Private/advertising_proxy_services.c
mDNSResponder-1310.40.42.tar.gz
[apple/mdnsresponder.git] / mDNSMacOSX / Private / advertising_proxy_services.c
1 /* advertising_proxy_services.h
2 *
3 * Copyright (c) 2020 Apple Computer, Inc. All rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 * This file contains the implementation of the SRP Advertising Proxy management
18 * API on MacOS, which is private API used to control and manage the advertising
19 * proxy.
20 */
21
22
23 #include <Block.h>
24 #include <os/log.h>
25 #include "xpc_clients.h"
26 #include "advertising_proxy_services.h"
27 #include "srp.h"
28
29 #if defined(TARGET_OS_TV)
30
31 //*************************************************************************************************************
32 // Globals
33
34 typedef struct _advertising_proxy_conn_ref_t
35 {
36 int ref_count; // This structure is refcounted
37 xpc_connection_t connection; // xpc_connection between client and daemon
38 advertising_proxy_reply app_callback; // Callback function ptr for Client
39 dispatch_queue_t client_queue; // Queue specified by client for scheduling its Callback
40 const char *command_name; // The advertising proxy command we've been given
41 } adv_conn_ref_t;
42
43 static void
44 advertising_proxy_ref_finalize(adv_conn_ref_t *conn_ref)
45 {
46 free(conn_ref);
47 }
48
49 void advertising_proxy_ref_dealloc(adv_conn_ref_t *conn_ref)
50 {
51 if (conn_ref == NULL)
52 {
53 os_log(OS_LOG_DEFAULT, "dns_services: advertising_proxy_ref_dealloc called with NULL advertising_proxy_conn_ref");
54 return;
55 }
56 conn_ref->app_callback = NULL;
57 if (conn_ref->connection != NULL) {
58 xpc_connection_cancel(conn_ref->connection);
59 }
60
61 // This is releasing the caller's reference. We may still have an internal reference.
62 RELEASE_HERE(conn_ref, advertising_proxy_ref_finalize);
63 os_log(OS_LOG_DEFAULT, "dns_services: advertising_proxy_ref_dealloc successfully released conn_ref");
64 }
65
66 static void
67 adv_connection_finalize(void *v_conn_ref)
68 {
69 adv_conn_ref_t *conn_ref = v_conn_ref;
70 os_log(OS_LOG_DEFAULT, "adv_connection_finalize: releasing conn_ref at %d", conn_ref->ref_count);
71 RELEASE_HERE(conn_ref, advertising_proxy_ref_finalize);
72 }
73
74 // Called for errors. The cancel argument is set to true if we need the callback to cancel the connection, as will be
75 // true for errors other than XPC_ERROR_CONNECTION_INVALID. If the callback doesn't call advertising_proxy_ref_dealloc,
76 // which will cancel the connection, then adv_connection_call_callback cancels it.
77 static void
78 adv_connection_call_callback(adv_conn_ref_t *conn_ref, xpc_object_t *event, int status, bool cancel)
79 {
80 int ref_count = conn_ref->ref_count;
81 conn_ref->app_callback(conn_ref, event, status);
82 if (conn_ref->ref_count > 1 && conn_ref->ref_count == ref_count) {
83 #ifndef __clang_analyzer__
84 RELEASE_HERE(conn_ref, advertising_proxy_ref_finalize);
85 #endif
86 if (cancel) {
87 xpc_connection_cancel(conn_ref->connection);
88 }
89 }
90
91 // We can never call the callback again after reporting an error.
92 conn_ref->app_callback = NULL;
93 }
94
95 static void
96 adv_event_handler(xpc_object_t event, adv_conn_ref_t *conn_ref)
97 {
98 if (event == XPC_ERROR_CONNECTION_INVALID) {
99 os_log(OS_LOG_DEFAULT, "adv_event_handler (%s): cleanup %p %p", conn_ref->command_name, conn_ref, conn_ref->connection);
100 if (conn_ref->app_callback != NULL) {
101 adv_connection_call_callback(conn_ref, event, kDNSSDAdvertisingProxyStatus_Disconnected, false);
102 } else {
103 os_log(OS_LOG_DEFAULT, "No callback");
104 }
105 if (conn_ref->connection != NULL) {
106 xpc_release(conn_ref->connection);
107 conn_ref->connection = NULL;
108 } else {
109 ERROR("adv_event_handler(%s): cleanup: conn_ref->connection is NULL when it shouldn't be!",
110 conn_ref->command_name);
111 }
112 } else if (xpc_get_type(event) == XPC_TYPE_DICTIONARY) {
113 if (conn_ref->app_callback != NULL) {
114 conn_ref->app_callback(conn_ref, event, kDNSSDAdvertisingProxyStatus_NoError);
115 } else {
116 os_log(OS_LOG_DEFAULT, "adv_event_handler (%s): no callback", conn_ref->command_name);
117 }
118 } else {
119 os_log(OS_LOG_DEFAULT, "adv_event_handler: Unexpected Connection Error [%s]",
120 xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION));
121 if (conn_ref->app_callback) {
122 adv_connection_call_callback(conn_ref, event, kDNSSDAdvertisingProxyStatus_DaemonNotRunning, true);
123 } else {
124 os_log(OS_LOG_DEFAULT, "adv_event_handler: no callback");
125 xpc_connection_cancel(conn_ref->connection);
126 }
127 }
128 }
129
130 // Creates a new advertising_proxy_ Connection Reference(advertising_proxy_conn_ref)
131 static advertising_proxy_error_type
132 adv_init_connection(adv_conn_ref_t **ref, const char *servname, xpc_object_t dict,
133 const char *command_name, advertising_proxy_reply app_callback, dispatch_queue_t client_queue,
134 const char *file, int line)
135 {
136 // Use an adv_conn_ref_t *on the stack to be captured in the blocks below, rather than
137 // capturing the advertising_proxy_conn_ref* owned by the client
138 adv_conn_ref_t *conn_ref = calloc(1, sizeof(adv_conn_ref_t));
139 if (conn_ref == NULL) {
140 os_log(OS_LOG_DEFAULT, "dns_services: init_connection() No memory to allocate!");
141 return kDNSSDAdvertisingProxyStatus_NoMemory;
142 }
143
144 // Initialize the advertising_proxy_conn_ref
145 dispatch_retain(client_queue);
146 conn_ref->command_name = command_name;
147 conn_ref->client_queue = client_queue;
148 conn_ref->app_callback = app_callback;
149 conn_ref->connection = xpc_connection_create_mach_service(servname, conn_ref->client_queue,
150 XPC_CONNECTION_MACH_SERVICE_PRIVILEGED);
151
152 if (conn_ref->connection == NULL)
153 {
154 os_log(OS_LOG_DEFAULT, "dns_services: init_connection() conn_ref/lib_q is NULL");
155 if (conn_ref != NULL) {
156 free(conn_ref);
157 }
158 return kDNSSDAdvertisingProxyStatus_NoMemory;
159 }
160
161 RETAIN_HERE(conn_ref); // For the event handler.
162 xpc_connection_set_event_handler(conn_ref->connection, ^(xpc_object_t event) { adv_event_handler(event, conn_ref); });
163 xpc_connection_set_finalizer_f(conn_ref->connection, adv_connection_finalize);
164 xpc_connection_set_context(conn_ref->connection, conn_ref);
165 xpc_connection_resume(conn_ref->connection);
166
167 xpc_connection_send_message_with_reply(conn_ref->connection, dict, conn_ref->client_queue,
168 ^(xpc_object_t event) { adv_event_handler(event, conn_ref); });
169
170 if (ref) {
171 *ref = conn_ref;
172 // For the caller
173 RETAIN(conn_ref);
174 }
175 return kDNSSDAdvertisingProxyStatus_NoError;
176 }
177
178 #define adv_send_command(ref, client_queue, command_name, dict, command, app_callback) \
179 adv_send_command_(ref, client_queue, command_name, dict, command, app_callback, __FILE__, __LINE__)
180 static advertising_proxy_error_type
181 adv_send_command_(adv_conn_ref_t **ref, dispatch_queue_t client_queue, const char *command_name,
182 xpc_object_t dict, const char *command, advertising_proxy_reply app_callback, const char *file, int line)
183 {
184 advertising_proxy_error_type errx = kDNSSDAdvertisingProxyStatus_NoError;
185
186 if (dict == NULL) {
187 os_log(OS_LOG_DEFAULT, "adv_send_command(%s): no memory for command dictionary.", command_name);
188 return kDNSSDAdvertisingProxyStatus_NoMemory;
189 }
190
191 // Sanity Checks
192 if (app_callback == NULL || client_queue == NULL)
193 {
194 os_log(OS_LOG_DEFAULT, "%s: NULL cti_connection_t OR Callback OR Client_Queue parameter",
195 command_name);
196 return kDNSSDAdvertisingProxyStatus_BadParam;
197 }
198
199 xpc_dictionary_set_string(dict, kDNSAdvertisingProxyCommand, command);
200
201 errx = adv_init_connection(ref, kDNSAdvertisingProxyService, dict, command_name, app_callback, client_queue, file, line);
202 if (errx) // On error init_connection() leaves *conn_ref set to NULL
203 {
204 os_log(OS_LOG_DEFAULT,
205 "%s: Since init_connection() returned %d error returning w/o sending msg",
206 command_name, errx);
207 return errx;
208 }
209
210 return errx;
211 }
212
213
214 advertising_proxy_error_type
215 advertising_proxy_enable(adv_conn_ref_t **conn_ref, dispatch_queue_t client_queue, advertising_proxy_reply callback)
216 {
217 advertising_proxy_error_type errx;
218 xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0);
219
220 errx = adv_send_command(conn_ref, client_queue, "advertising_proxy_enable",
221 dict, kDNSAdvertisingProxyEnable, callback);
222 xpc_release(dict);
223 return errx;
224 }
225
226 advertising_proxy_error_type
227 advertising_proxy_flush_entries(adv_conn_ref_t **conn_ref,
228 dispatch_queue_t client_queue, advertising_proxy_reply callback)
229 {
230 advertising_proxy_error_type errx;
231 xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0);
232
233 errx = adv_send_command(conn_ref, client_queue, "advertising_proxy_flush_entries",
234 dict, kDNSAdvertisingProxyFlushEntries, callback);
235 xpc_release(dict);
236 return errx;
237 }
238
239 advertising_proxy_error_type
240 advertising_proxy_get_service_list(adv_conn_ref_t **conn_ref,
241 dispatch_queue_t client_queue, advertising_proxy_reply callback)
242 {
243 advertising_proxy_error_type errx;
244 xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0);
245
246 errx = adv_send_command(conn_ref, client_queue, "advertising_proxy_get_service_list",
247 dict, kDNSAdvertisingProxyListServices, callback);
248 xpc_release(dict);
249 return errx;
250 }
251
252 advertising_proxy_error_type
253 advertising_proxy_block_service(adv_conn_ref_t **conn_ref,
254 dispatch_queue_t client_queue, advertising_proxy_reply callback)
255 {
256 advertising_proxy_error_type errx;
257 xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0);
258
259 errx = adv_send_command(conn_ref, client_queue, "advertising_proxy_block_service",
260 dict, kDNSAdvertisingProxyBlockService, callback);
261 xpc_release(dict);
262 return errx;
263 }
264
265 advertising_proxy_error_type
266 advertising_proxy_unblock_service(adv_conn_ref_t **conn_ref,
267 dispatch_queue_t client_queue, advertising_proxy_reply callback)
268 {
269 advertising_proxy_error_type errx;
270 xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0);
271
272 errx = adv_send_command(conn_ref, client_queue, "advertising_proxy_unblock_service",
273 dict, kDNSAdvertisingProxyUnblockService, callback);
274 xpc_release(dict);
275 return errx;
276 }
277
278 advertising_proxy_error_type
279 advertising_proxy_regenerate_ula(adv_conn_ref_t **conn_ref,
280 dispatch_queue_t client_queue, advertising_proxy_reply callback)
281 {
282 advertising_proxy_error_type errx;
283 xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0);
284
285 errx = adv_send_command(conn_ref, client_queue, "advertising_proxy_regenerate_ula",
286 dict, kDNSAdvertisingProxyRegenerateULA, callback);
287 xpc_release(dict);
288 return errx;
289 }
290 #endif // defined(TARGET_OS_TV)
291
292 // Local Variables:
293 // mode: C
294 // tab-width: 4
295 // c-file-style: "bsd"
296 // c-basic-offset: 4
297 // fill-column: 120
298 // indent-tabs-mode: nil
299 // End: