You've already forked xpc-discovery
mirror of
https://github.com/t2linux/xpc-discovery.git
synced 2026-04-30 13:54:09 -07:00
ef8f76807d
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
227 lines
7.0 KiB
C
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;
|
|
}
|