Files
Orlando Chamberlain ef8f76807d support getting individual ports etc
we support getting the port of an individual service, all services, and
also just dumping the raw data in a jsonlike format. Also made the
program terminate once it recieves the data.

$ ./xpc --service com.apple.eos.BiometricKit
com.apple.eos.BiometricKit      49238
$ ./xpc --dump
{"Services": {"com.ap.... (this prints everything)
$ ./xpc --list
com.apple.xpc.remote.mobile_obliteration        49242
com.apple.videoprocessingd.encode.remote        49256
...
com.apple.sysdiagnose.stackshot.remote  49248
com.apple.nfcd.relay.control    49258
2026-01-18 00:32:49 +11:00

227 lines
7.0 KiB
C

#include <stdio.h>
#include <getopt.h>
#include <malloc.h>
#include <string.h>
#include <event2/event.h>
#include <event2/dns.h>
#include <event2/buffer.h>
#include <event2/bufferevent.h>
#include <arpa/inet.h>
#include <nghttp2/nghttp2.h>
#include <netinet/tcp.h>
#include <sys/param.h>
#include <xpc/xpc.h>
#include <xpc/xpc_debug.h>
#include <xpc/xpc_serialization.h>
#include "rxpc/session_libevent.h"
#include "rxpc/session.h"
#include "rxpc/stream.h"
#define DEVICE_IPADDR "fe80::aede:48ff:fe33:4455"
#define DEVICE_INTERFACE "t2_ncm" // this assumes you have a udev rule
#define DISCOVERY_PORT 59602
static struct event_base *global_evbase = NULL; // TODO: is there a better way to do this
int print_service_port(xpc_object_t o, const char *serviceName) {
xpc_object_t services = xpc_dictionary_get_value(o, "Services");
if (!services)
return -1;
xpc_object_t service = xpc_dictionary_get_value(services,serviceName);
if (!service)
return -1;
xpc_object_t port = xpc_dictionary_get_value(service, "Port");
if (!port)
return -1;
const char *portStr = xpc_string_get_string_ptr(port);
if (!portStr)
return -1;
printf("%s\t%s\n", serviceName, portStr);
return 0;
}
xpc_object_t servicesObj = NULL;
bool verbose = false;
void rxpc_session_message_print(struct rxpc_stream *stream, struct rxpc_msg_header *header, const void *data) {
const char *msg_type;
if (verbose)
printf("rxpc: got message { type = %u, flags = %u, msg_id = %" PRIu64 " }\n",
header->type, header->flags, header->msg_id);
if (header->length > 0) {
xpc_object_t o = xpc_deserialize(data, header->length);
xpc_object_t message_type = xpc_dictionary_get_value(o, "MessageType");
if (message_type && !strncmp(
xpc_string_get_string_ptr(message_type), "Handshake", 10)) {
servicesObj = o;
event_base_loopexit(global_evbase,0);
// don't free o here, main() will do that later.
} else {
xpc_free(o);
}
}
}
static void reply_hello(struct rxpc_stream *stream){
rxpc_stream_send(stream, RXPC_TYPE_HELLO, RXPC_FLAG_REPLY_CHANNEL, 1, NULL);
}
static void send_hello(struct rxpc_stream *stream){
struct rxpc_stream_callbacks cbs = {0};
xpc_object_t dict;
dict = xpc_dictionary_create(NULL, NULL, 0);
// optional: xpc_dictionary_set_int64("ServiceVersion", value);
rxpc_stream_send(stream, RXPC_TYPE_HELLO, 0, 0, dict);
xpc_free(dict);
cbs.opened = reply_hello;
stream->session->stream_reply = rxpc_stream_open(stream->session, &cbs);
rxpc_session_send_pending(stream->session);
}
int main(int argc, char **argv) {
struct event_base *evbase;
struct evdns_base *evdns;
struct bufferevent *socket;
struct rxpc_libevent_session *session;
struct rxpc_stream_callbacks cbs;
int opt, option_index;
int port = DISCOVERY_PORT;
char address[NI_MAXHOST] = DEVICE_IPADDR;
char interface[NI_MAXHOST] = DEVICE_INTERFACE;
char full_address[NI_MAXHOST];
size_t addr;
char *serviceName = NULL;
bool dump = false;
bool list = false;
/* We make sure the network interface is specified, so that
* we actually connect to the t2. A udev rule renaming the
* T2's USB CDC NCM interface to "t2_ncm" is expected by default.
*/
while (1)
{
// TODO: also support short options
static struct option long_options[] =
{
{"port", required_argument, 0, 'p'},
{"address", required_argument, 0, 'a'},
{"interface", required_argument, 0, 'i'},
{"help", no_argument, 0, 'h'},
{"dump", no_argument, 0, 'd'},
{"list", no_argument, 0, 'l'},
{"service", required_argument, 0, 's'},
{"verbose", no_argument, 0, 'v'},
{0, 0, 0, 0}
};
option_index = 0;
opt = getopt_long(argc, argv, "p:a:i:h:d:s:l:v:",
long_options, &option_index);
if (opt == -1)
break;
switch (opt)
{
case 'p':
port = strtol(optarg, NULL, 0);
break;
case 'a':
strncpy(address, optarg, sizeof(address-8));
break;
case 'i':
strncpy(interface, optarg, sizeof(interface));
break;
case 'd':
dump = true;
break;
case 'v':
verbose = true;
break;
case 's':
serviceName = malloc(strlen(optarg));
strcpy(serviceName, optarg);
break;
case 'l':
list = true;
break;
case 'h':
case '?':
printf("usage: %s [--verbose] [--port=remote_port] [--address=ip_address]"
" [--interface=net_interface_name]\n", argv[0]);
printf("--help Print this help\n");
printf("--dump Print the raw xpc data in JSONish format\n");
printf("--list Print the name and port of each service\n");
printf("--service [name] Print the name and port of a specific service\n");
break;
default:
abort();
}
}
strcat(address, "%");
strncat(address, interface, sizeof(address)-strlen(address));
if (verbose)
printf("Connecting to: %s:%d\n", address, port);
evbase = event_base_new();
if (!evbase) {
fprintf(stderr, "event_base_new failed\n");
return -ENOMEM;
}
evdns = evdns_base_new(evbase, 1);
cbs.opened = send_hello;
cbs.message = rxpc_session_message_print;
session = rxpc_libevent_session_create(evbase, &cbs);
if (bufferevent_socket_connect_hostname(session->bev, evdns, AF_UNSPEC, address, port)) {
fprintf(stderr, "bufferevent_socket_connect failed\n");
return -ENOMEM;;
}
global_evbase = evbase;
event_base_loop(evbase, 0);
evdns_base_free(evdns, 1);
event_base_free(evbase);
int ret = 0;
if (servicesObj) {
if (dump) {
xpc_debug_print_helper(servicesObj, stdout);
} else if (list) {
xpc_object_t services = xpc_dictionary_get_value(servicesObj, "Services");
const char *key;
int i = 0;
key = xpc_dictionary_get_key_by_index(services, i);
while (key) {
ret = print_service_port(servicesObj, key);
if (ret)
break;
i++;
key = xpc_dictionary_get_key_by_index(services, i);
}
} else if (serviceName) {
ret = print_service_port(servicesObj, serviceName);
}
xpc_free(servicesObj);
if (serviceName) {
free(serviceName);
}
}
return ret;
}