summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGravatar Nikias Bassen2025-06-12 07:30:16 +0200
committerGravatar Nikias Bassen2025-06-12 07:30:16 +0200
commiteeb320dc055716e0ebeeb8845c9ca8c4e2451124 (patch)
tree0030be86adaafbdd5aaefa2e114e72ca8f6f747d /src
parentc65b19d8edfcc82230205a76da95d3a4a8a282ec (diff)
downloadlibimobiledevice-eeb320dc055716e0ebeeb8845c9ca8c4e2451124.tar.gz
libimobiledevice-eeb320dc055716e0ebeeb8845c9ca8c4e2451124.tar.bz2
Add os_trace_relay service implementation
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am1
-rw-r--r--src/ostrace.c458
-rw-r--r--src/ostrace.h37
3 files changed, 496 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 58cf07c..1ee9be8 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -58,6 +58,7 @@ libimobiledevice_1_0_la_SOURCES = \
58 companion_proxy.c companion_proxy.h \ 58 companion_proxy.c companion_proxy.h \
59 reverse_proxy.c reverse_proxy.h \ 59 reverse_proxy.c reverse_proxy.h \
60 syslog_relay.c syslog_relay.h \ 60 syslog_relay.c syslog_relay.h \
61 ostrace.c ostrace.h \
61 bt_packet_logger.c bt_packet_logger.h 62 bt_packet_logger.c bt_packet_logger.h
62 63
63if WIN32 64if WIN32
diff --git a/src/ostrace.c b/src/ostrace.c
new file mode 100644
index 0000000..0fdd147
--- /dev/null
+++ b/src/ostrace.c
@@ -0,0 +1,458 @@
1/*
2 * ostrace.c
3 * com.apple.os_trace_relay service implementation.
4 *
5 * Copyright (c) 2020-2025 Nikias Bassen, All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25#include <string.h>
26#include <stdlib.h>
27
28#include <plist/plist.h>
29
30#include "ostrace.h"
31#include "lockdown.h"
32#include "common/debug.h"
33#include "endianness.h"
34
35struct ostrace_worker_thread {
36 ostrace_client_t client;
37 ostrace_activity_cb_t cbfunc;
38 void *user_data;
39};
40
41/**
42 * Convert a service_error_t value to a ostrace_error_t value.
43 * Used internally to get correct error codes.
44 *
45 * @param err An service_error_t error code
46 *
47 * @return A matching ostrace_error_t error code,
48 * OSTRACE_E_UNKNOWN_ERROR otherwise.
49 */
50static ostrace_error_t ostrace_error(service_error_t err)
51{
52 switch (err) {
53 case SERVICE_E_SUCCESS:
54 return OSTRACE_E_SUCCESS;
55 case SERVICE_E_INVALID_ARG:
56 return OSTRACE_E_INVALID_ARG;
57 case SERVICE_E_MUX_ERROR:
58 return OSTRACE_E_MUX_ERROR;
59 case SERVICE_E_SSL_ERROR:
60 return OSTRACE_E_SSL_ERROR;
61 case SERVICE_E_NOT_ENOUGH_DATA:
62 return OSTRACE_E_NOT_ENOUGH_DATA;
63 case SERVICE_E_TIMEOUT:
64 return OSTRACE_E_TIMEOUT;
65 default:
66 break;
67 }
68 return OSTRACE_E_UNKNOWN_ERROR;
69}
70
71ostrace_error_t ostrace_client_new(idevice_t device, lockdownd_service_descriptor_t service, ostrace_client_t * client)
72{
73 *client = NULL;
74
75 if (!device || !service || service->port == 0 || !client || *client) {
76 debug_info("Incorrect parameter passed to ostrace_client_new.");
77 return OSTRACE_E_INVALID_ARG;
78 }
79
80 debug_info("Creating ostrace_client, port = %d.", service->port);
81
82 service_client_t parent = NULL;
83 ostrace_error_t ret = ostrace_error(service_client_new(device, service, &parent));
84 if (ret != OSTRACE_E_SUCCESS) {
85 debug_info("Creating base service client failed. Error: %i", ret);
86 return ret;
87 }
88
89 ostrace_client_t client_loc = (ostrace_client_t) malloc(sizeof(struct ostrace_client_private));
90 client_loc->parent = parent;
91 client_loc->worker = THREAD_T_NULL;
92
93 *client = client_loc;
94
95 debug_info("ostrace_client successfully created.");
96 return 0;
97}
98
99ostrace_error_t ostrace_client_start_service(idevice_t device, ostrace_client_t * client, const char* label)
100{
101 ostrace_error_t err = OSTRACE_E_UNKNOWN_ERROR;
102 service_client_factory_start_service(device, OSTRACE_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(ostrace_client_new), &err);
103 return err;
104}
105
106ostrace_error_t ostrace_client_free(ostrace_client_t client)
107{
108 if (!client)
109 return OSTRACE_E_INVALID_ARG;
110 ostrace_stop_activity(client);
111 ostrace_error_t err = ostrace_error(service_client_free(client->parent));
112 free(client);
113
114 return err;
115}
116
117/*
118ostrace_error_t ostrace_receive_with_timeout(ostrace_client_t client, char* data, uint32_t size, uint32_t *received, unsigned int timeout)
119{
120 ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR;
121 int bytes = 0;
122
123 if (!client || !data || (size == 0)) {
124 return OSTRACE_E_INVALID_ARG;
125 }
126
127 res = ostrace_error(service_receive_with_timeout(client->parent, data, size, (uint32_t*)&bytes, timeout));
128 if (res != OSTRACE_E_SUCCESS && res != OSTRACE_E_TIMEOUT && res != OSTRACE_E_NOT_ENOUGH_DATA) {
129 debug_info("Could not read data, error %d", res);
130 }
131 if (received) {
132 *received = (uint32_t)bytes;
133 }
134
135 return res;
136}*/
137
138static ostrace_error_t ostrace_send_plist(ostrace_client_t client, plist_t plist)
139{
140 ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR;
141 uint32_t blen = 0;
142 char* bin = NULL;
143 uint32_t sent = 0;
144 uint32_t swapped_len = 0;
145
146 if (!client || !plist) {
147 return OSTRACE_E_INVALID_ARG;
148 }
149
150 plist_to_bin(plist, &bin, &blen);
151 swapped_len = htobe32(blen);
152
153 res = ostrace_error(service_send(client->parent, (char*)&swapped_len, 4, &sent));
154 if (res == OSTRACE_E_SUCCESS) {
155 res = ostrace_error(service_send(client->parent, bin, blen, &sent));
156 }
157 free(bin);
158 return res;
159}
160
161static ostrace_error_t ostrace_receive_plist(ostrace_client_t client, plist_t *plist)
162{
163 ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR;
164 uint8_t msgtype = 0;
165 uint32_t received = 0;
166 res = ostrace_error(service_receive(client->parent, (char*)&msgtype, 1, &received));
167 if (res != OSTRACE_E_SUCCESS) {
168 debug_info("Failed to read message type from service");
169 return res;
170 }
171 uint32_t rlen = 0;
172 res = ostrace_error(service_receive(client->parent, (char*)&rlen, 4, &received));
173 if (res != OSTRACE_E_SUCCESS) {
174 debug_info("Failed to read message size from service");
175 return res;
176 }
177
178 if (msgtype == 1) {
179 rlen = be32toh(rlen);
180 } else if (msgtype == 2) {
181 rlen = le32toh(rlen);
182 } else {
183 debug_info("Unexpected message type %d", msgtype);
184 return OSTRACE_E_UNKNOWN_ERROR;
185 }
186 debug_info("got length %d", rlen);
187
188 char* buf = (char*)malloc(rlen);
189 res = ostrace_error(service_receive(client->parent, buf, rlen, &received));
190 if (res != OSTRACE_E_SUCCESS) {
191 return res;
192 }
193
194 plist_t reply = NULL;
195 plist_err_t perr = plist_from_memory(buf, received, &reply, NULL);
196 free(buf);
197 if (perr != PLIST_ERR_SUCCESS) {
198 return OSTRACE_E_UNKNOWN_ERROR;
199 }
200 *plist = reply;
201 return OSTRACE_E_SUCCESS;
202}
203
204static ostrace_error_t _ostrace_check_result(plist_t reply)
205{
206 ostrace_error_t res = OSTRACE_E_REQUEST_FAILED;
207 if (!reply) {
208 return res;
209 }
210 plist_t p_status = plist_dict_get_item(reply, "Status");
211 if (!p_status) {
212 return res;
213 }
214 const char* status = plist_get_string_ptr(p_status, NULL);
215 if (!status) {
216 return res;
217 }
218 if (!strcmp(status, "RequestSuccessful")) {
219 res = OSTRACE_E_SUCCESS;
220 }
221 return res;
222}
223
224void *ostrace_worker(void *arg)
225{
226 ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR;
227 struct ostrace_worker_thread *oswt = (struct ostrace_worker_thread*)arg;
228
229 if (!oswt)
230 return NULL;
231
232 uint8_t msgtype = 0;
233 uint32_t received = 0;
234
235 debug_info("Running");
236
237 while (oswt->client->parent) {
238 res = ostrace_error(service_receive(oswt->client->parent, (char*)&msgtype, 1, &received));
239 if (res == OSTRACE_E_TIMEOUT) {
240 debug_info("Nothing received, retrying\n");
241 continue;
242 }
243 if (res != OSTRACE_E_SUCCESS) {
244 debug_info("Failed to read message type from service");
245 break;
246 }
247 uint32_t rlen = 0;
248 res = ostrace_error(service_receive(oswt->client->parent, (char*)&rlen, 4, &received));
249 if (res != OSTRACE_E_SUCCESS) {
250 debug_info("Failed to read message size from service");
251 break;
252 }
253
254 if (msgtype == 1) {
255 rlen = be32toh(rlen);
256 } else if (msgtype == 2) {
257 rlen = le32toh(rlen);
258 } else {
259 debug_info("Unexpected message type %d", msgtype);
260 break;
261 }
262
263 debug_info("got length %d", rlen);
264
265 unsigned char* buf = (unsigned char*)malloc(rlen);
266 res = ostrace_error(service_receive(oswt->client->parent, (char*)buf, rlen, &received));
267 if (res != OSTRACE_E_SUCCESS) {
268 debug_info("Failed to receive %d bytes, error %d", rlen, res);
269 break;
270 }
271 if (received < rlen) {
272 debug_info("Failed to receive all data, got %d/%d", received, rlen);
273 break;
274 }
275 oswt->cbfunc(buf, received, oswt->user_data);
276 }
277
278 if (oswt) {
279 free(oswt);
280 }
281
282 debug_info("Exiting");
283
284 return NULL;
285}
286
287ostrace_error_t ostrace_start_activity(ostrace_client_t client, plist_t options, ostrace_activity_cb_t callback, void* user_data)
288{
289 if (!client || !callback)
290 return OSTRACE_E_INVALID_ARG;
291
292 ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR;
293
294 if (client->worker) {
295 debug_info("Another ostrace activity thread appears to be running already.");
296 return res;
297 }
298
299 plist_t dict = plist_new_dict();
300 plist_dict_set_item(dict, "Pid", plist_new_uint(0x0FFFFFFFF));
301 plist_dict_set_item(dict, "MessageFilter", plist_new_uint(0xFFFF));
302 plist_dict_set_item(dict, "StreamFlags", plist_new_uint(0x3C));
303 if (options) {
304 plist_dict_merge(&dict, options);
305 }
306 plist_dict_set_item(dict, "Request", plist_new_string("StartActivity"));
307
308 res = ostrace_send_plist(client, dict);
309 plist_free(dict);
310 if (res != OSTRACE_E_SUCCESS) {
311 return res;
312 }
313
314 dict = NULL;
315 res = ostrace_receive_plist(client, &dict);
316 if (res != OSTRACE_E_SUCCESS) {
317 return res;
318 }
319 res = _ostrace_check_result(dict);
320 if (res != OSTRACE_E_SUCCESS) {
321 return res;
322 }
323
324 /* start worker thread */
325 struct ostrace_worker_thread *oswt = (struct ostrace_worker_thread*)malloc(sizeof(struct ostrace_worker_thread));
326 if (oswt) {
327 oswt->client = client;
328 oswt->cbfunc = callback;
329 oswt->user_data = user_data;
330
331 if (thread_new(&client->worker, ostrace_worker, oswt) == 0) {
332 res = OSTRACE_E_SUCCESS;
333 }
334 }
335
336 return res;
337}
338
339ostrace_error_t ostrace_stop_activity(ostrace_client_t client)
340{
341 if (client->worker) {
342 /* notify thread to finish */
343 service_client_t parent = client->parent;
344 client->parent = NULL;
345 /* join thread to make it exit */
346 thread_join(client->worker);
347 thread_free(client->worker);
348 client->worker = THREAD_T_NULL;
349 client->parent = parent;
350 }
351
352 return OSTRACE_E_SUCCESS;
353}
354
355ostrace_error_t ostrace_get_pid_list(ostrace_client_t client, plist_t* list)
356{
357 ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR;
358 plist_t dict = plist_new_dict();
359 plist_dict_set_item(dict, "Request", plist_new_string("PidList"));
360
361 if (!client || !list) {
362 return OSTRACE_E_INVALID_ARG;
363 }
364
365 res = ostrace_send_plist(client, dict);
366 plist_free(dict);
367 if (res != OSTRACE_E_SUCCESS) {
368 return res;
369 }
370
371 plist_t reply = NULL;
372 res = ostrace_receive_plist(client, &reply);
373 if (res != OSTRACE_E_SUCCESS) {
374 return res;
375 }
376 res = _ostrace_check_result(reply);
377 if (res != OSTRACE_E_SUCCESS) {
378 return res;
379 }
380
381 plist_t payload = plist_dict_get_item(reply, "Payload");
382 if (!payload) {
383 return OSTRACE_E_REQUEST_FAILED;
384 }
385 *list = plist_copy(payload);
386 plist_free(reply);
387
388 return OSTRACE_E_SUCCESS;
389}
390
391ostrace_error_t ostrace_create_archive(ostrace_client_t client, plist_t options, ostrace_archive_write_cb_t callback, void* user_data)
392{
393 ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR;
394 if (!client || !callback) {
395 return OSTRACE_E_INVALID_ARG;
396 }
397 plist_t dict = plist_new_dict();
398 if (options) {
399 plist_dict_merge(&dict, options);
400 }
401 plist_dict_set_item(dict, "Request", plist_new_string("CreateArchive"));
402
403 res = ostrace_send_plist(client, dict);
404 plist_free(dict);
405 if (res != OSTRACE_E_SUCCESS) {
406 return res;
407 }
408
409 plist_t reply = NULL;
410 res = ostrace_receive_plist(client, &reply);
411 if (res != OSTRACE_E_SUCCESS) {
412 return res;
413 }
414
415 res = _ostrace_check_result(reply);
416 if (res != OSTRACE_E_SUCCESS) {
417 return res;
418 }
419
420 debug_info("Receiving archive...\n");
421 while (1) {
422 uint8_t msgtype = 0;
423 uint32_t received = 0;
424 res = ostrace_error(service_receive(client->parent, (char*)&msgtype, 1, &received));
425 if (res != OSTRACE_E_SUCCESS) {
426 debug_info("Could not read message type from service: %d", res);
427 break;
428 }
429 if (msgtype != 3) {
430 debug_info("Unexpected packet type %d", msgtype);
431 return OSTRACE_E_REQUEST_FAILED;
432 }
433 uint32_t rlen = 0;
434 res = ostrace_error(service_receive(client->parent, (char*)&rlen, 4, &received));
435 if (res != OSTRACE_E_SUCCESS) {
436 debug_info("Failed to read message size from service");
437 break;
438 }
439
440 rlen = le32toh(rlen);
441 debug_info("got length %d", rlen);
442
443 unsigned char* buf = (unsigned char*)malloc(rlen);
444 res = ostrace_error(service_receive(client->parent, (char*)buf, rlen, &received));
445 if (res != OSTRACE_E_SUCCESS) {
446 debug_info("Could not read data from service: %d", res);
447 break;
448 }
449 if (callback(buf, received, user_data) < 0) {
450 debug_info("Aborted through callback");
451 return OSTRACE_E_REQUEST_FAILED;
452 }
453 }
454 debug_info("Done.\n");
455
456 return OSTRACE_E_SUCCESS;
457}
458
diff --git a/src/ostrace.h b/src/ostrace.h
new file mode 100644
index 0000000..dcc3e8d
--- /dev/null
+++ b/src/ostrace.h
@@ -0,0 +1,37 @@
1/*
2 * ostrace.h
3 * com.apple.os_trace_relay service header file.
4 *
5 * Copyright (c) 2020-2025 Nikias Bassen, All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#ifndef _OSTRACE_H
23#define _OSTRACE_H
24
25#include "idevice.h"
26#include "libimobiledevice/ostrace.h"
27#include "service.h"
28#include <libimobiledevice-glue/thread.h>
29
30struct ostrace_client_private {
31 service_client_t parent;
32 THREAD_T worker;
33};
34
35void *ostrace_worker(void *arg);
36
37#endif